new fork: rip out drawing support
[?]
Aug 14, 2022, 4:17 PM
MD3W5IRAC6UQALQE4LJC52VQNDO3I3HXF3XE2XHDABXBYJBUVAXQCDependencies
- [2]
WI7R44TDbugfix - [3]
2WGRQI5Emissing shape modes in a couple more places - [4]
JZKEIKO6freudian typo - [5]
E5FYDACSa likely source of issues - [6]
UN7L3DNNavoid some string concatenations - [7]
WIPDCP4Ustop recording points for arcs - [8]
F4QQIBEHclarify what "large files" means - [9]
SBS2F7GRlink to export tool - [10]
C4VTBATA. - [11]
FM7UDV2Gno, bring back that defense - [12]
225JKBBOclean up a cross-test leakage - [13]
3OTESDW6move drawing.starty into line cache - [14]
FNJF2FMQbugfix: online help - [15]
SPNMXTYRhave file API operate on state object - [16]
IFTYOERMline.y -> line_cache.starty in a few more places - [17]
KJQ5FEYVround coordinates to integers in a few places - [18]
QJB4UHNVround one coordinate - [19]
JIK7ZRYIbugfix: imprecision in drawing - [20]
EAEGCJV5rename - [21]
7QQXO4YYbugfix: handle drawings when updating screen top - [22]
7PZ4CQFVsearch: transparently handle drawings everywhere - [23]
AMOPICKVbugfix: check after cursor on same line when searching upwards - [24]
5STHSG4Uremove some duplication - [25]
PNBKVYZ4more cogent onboarding instructions - [26]
6YWPSNUKnew mirror - [27]
YT5P6TO6bugfix: save previous file when dropping a new one on - [28]
C6QTJYA4keep online help inside of drawing - [29]
34TC5SYKrecord another known issue I don't know how to fix yet - [30]
K4OBZSHEadd args to some functions - [31]
QYIFOHW3first test! - [32]
H2DPLWMVsnapshot: wrapping long lines at word boundaries - [33]
ILOA5BYFseparate data structure for each line's cache data - [34]
R3JZDBI2drop heavyweight near check on file load/store - [35]
D4FEFHQCflesh out Readme - [36]
2CH77LZCadd args to some functions - [37]
LUNH47XXmake text and drawings the same width - [38]
MXA3RZYKdeduce left/right from state where possible - [39]
M6TH7VSZrip out notion of Line_width - [40]
3HVBAZPAadd state arg to a few functions - [41]
APYPFFS3call edit rather than App callbacks in tests - [42]
AD34IX2Zcouple more tests - [43]
5T2E3PDVcouple of bugfixes to file-handling - [44]
HYEAFRZ2split mouse_pressed events between Text and Drawing - [45]
QXVD2RIFadd state arg to Drawing.mouse_released - [46]
VCMS2CWTbugfix: escape key to hide online help - [47]
VTYCPKNHdead code - [48]
AJB4LFRBtry to maintain a reasonable line width - [49]
TLOAPLBJadd a license - [50]
BOFNXP5Gclicking now moves the cursor even on long, wrapped lines - [51]
ZHLO7K3Madd args to some functions - [52]
ZPUQSPQPextract a few methods - [53]
WAQFRM2Ufix contact link - [54]
2L4DL7PGgo through App in a couple more places - [55]
WJBZZQE4fold together two largely similar cases - [56]
HMODUNJEscroll on backspace - [57]
H22OAXWEcouple of TODOs - [58]
R3XGABERchunk up some long lines - [59]
BJ2C6F2Bignore 'name' mode in a few places - [60]
7JH2ZT3Fadd state arg to Drawing.draw - [61]
JFFUF5ALoverride mouse state lookups in tests - [62]
FS2ITYYHrecord a known issue - [63]
5DOTWNVMright margin - [64]
PFT5Y2ZYmove - [65]
4CXVIEBSadd args to some functions - [66]
QCPXQ2E3add state arg to a few functions - [67]
PTT4K4EUuse the provided args everywhere - [68]
3QNOKBFMbeginnings of a test harness - [69]
RSZD5A7Gforgot to add json.lua - [70]
42LVB4DEtest: naming a point - [71]
BERHYBXMfix help for rectangles and squares - [72]
253TWKDAclean up - [73]
KZ5GAYRPthis fixes the immediate regression - [74]
25V2GA6Jtaking stock - [75]
P4376EXKadd state arg to few functions - [76]
KTZQ57HVreplace globals with args in a few functions - [77]
UV4EWOLYadd args to some functions - [78]
AVQ5MC5Dfinish uppercasing all globals - [79]
HIKLULFQextract a function - [80]
OGUV4HSAremove some memory leaks from rendered fragments - [81]
YCDYGEZUinclude drawing index in a few places - [82]
JAXPXLEBset current_drawing_index with current_drawing - [83]
DSLD74DKlots more tests - [84]
ZZ2B5RPQextract variables for drawing padding - [85]
S2YQBEYCsnapshot: test for a new regression - [86]
CUIV2LE5some typos - [87]
EM276IH3make a function oblivious to line data structure - [88]
PYLBFWWEoverzealous search-and-replace - [89]
2Y5GGGJ4correct a mis-named threshold - [90]
YGCT2D2Ostart loading settings as applicable - [91]
HPVT467Winitialize contains test state - [92]
FFBIY74Nbugfix: 'escape' to cancel a stroke - [93]
IMEJA43Lsnapshot - [94]
2H67P75Xswitch arg for a function - [95]
DRFE3B3Zmouse buttons are integers, not strings - [96]
66X36NZNa little more prose describing manual_tests - [97]
CIQN2MDEbugfix: typing a capital letter deletes selection - [98]
DHI6IJCNselecting text and deleting selections - [99]
65HNIAOSmake freehand drawings smoother - [100]
T4FRZSYLdelete an ancient, unused file - [101]
DJSIRUMDa more radical attempt at ignoring nil y's - [102]
XRLJDW3Wcasting about for more helpers to extract.. - [103]
LYN3L74Wcorrect commit f3abc2cbf2 - [104]
E6TMJY2Tlighter color for in-progress strokes - [105]
R5QXEHUIsomebody stop me - [106]
R22PA3XRfix a second BSOD in #4 :/ - [107]
7EQLPB3Obugfix: don't delete selection when moving cursor - [108]
62JEPVQ3bugfix: backspace from start of final line - [109]
YPHKZVWMextract a new variable - [110]
UZVWYRTYmissing temporary modes in a couple more places - [111]
MYC7XR5Qbugfix: lines that aren't drawn from the start - [112]
5ZA3BRNYadd state arg to a few functions - [113]
BLWAYPKVextract a module - [114]
DLQMM265scroll past first page - [115]
FYS7TCDWbugfix - [116]
SPSW74Y5add state arg to Text.keychord_pressed - [117]
CLRJI4QKbugfix: backspace from start of file - [118]
XNFTJHC4split keyboard handling between Text and Drawing - [119]
FKNXK2OAswitch to line index in a function - [120]
V5MJRFOZbugfix: down arrow doesn't scroll up unnecessarily - [121]
KMRJOSLYbugfix: delete selection before pasting - [122]
AH744RFRshow when we're naming a point - [123]
TGZAJUEFbring back a set of constants - [124]
TGHAJBESuse line cache for drawings as well - [125]
EGH7XDBKsupport non-text lines in Text.to2 - [126]
6J3NXBYGaffordance to adjust width for word wrap - [127]
CE4LZV4Tdrop last couple of manual tests - [128]
5L7K4GBDclicking to the right of a wrapped line - [129]
TVCPXAAUrename - [130]
BJ5X5O4Alet's prevent the text cursor from ever getting on a drawing - [131]
NHA7RUFImove current mode indicator slightly - [132]
XX7G2FFJintermingle freehand line drawings with text - [133]
2ZYV7D3Whandle tab characters - [134]
BXJMGTV2hoist couple of variables out - [135]
MP2TBKU6bugfix: crash in Text.up() after return - [136]
6LIPEQ5Imore robust transitions to temporary modes - [137]
KOYAJWE4extract a couple more methods - [138]
6LJZN727handle chords - [139]
L6XA5EY2test: moving a point - [140]
NQH7DEEWbugfix: missed rename in one file - [141]
OWK3U6VDtests for drawing polygons - [142]
EMRPLZPWdrop an arg from a function - [143]
PTDO2SOTadd state arg to schedule_save - [144]
F3OOGMMEswitch freehand hotkey to eliminate conflict with search - [145]
VJ77YABHmore efficient undo/redo - [146]
PR4KIAZDfirst stab at equally hacky cursor down support - [147]
4KC7I3E2make colors easier to edit - [148]
3QQZ7W4Ebring couple more globals back to the app level - [149]
GGJEDJOOadd args to some functions - [150]
UHB4GARJleft/right margin -> left/right coordinates - [151]
HALS7E5Umore clearly skip prints before screen top - [152]
6VQIWTQUstandardize on ordering of cases - [153]
C45WCXJ2keep drawings within the line width slider as well - [154]
PVEZKGACbugfix: recompute screen lines in backspace/delete - [155]
ODLKHO7Bswitch to line index in a function - [156]
QFC3WRDZchunking by simple local variable - [157]
3RGHOJ25DRY some code - [158]
7DPPMI2Uanother integer coordinate - [159]
WLJCIXYMadd state arg to a few functions - [160]
KEPVDTCG. - [161]
CPZGQT72go through and fix similar issues - [162]
4CTZOJPCstop pretending globals are local - [163]
3ZSUBI57drop some redundant args from Text.draw - [164]
SRVDX4I5local var - [165]
7OUJM7DLone missing transition between shape modes - [166]
YJGADSGKdelete unused arg - [167]
SVJZZDC3snapshot - no, that's all wrong - [168]
2K2YDMFHignore 'deleted' shapes when saving to disk - [169]
BXYVMS4Ause available variables - [170]
2RXZ3PGObeginning of a new approach to scroll+wrap - [171]
IEHG6OROnew fork for #1 - [172]
242L3OQXbugfix: ensure Cursor_line is always on a text line - [173]
23MA4T3Gadd state arg to Drawing.keychord_pressed - [174]
WZFMGVDTswitch to line index in a function - [175]
6UZ2JNZEyet another key conflict - [176]
NDHQN23Gdone passing left/right margins everywhere - [177]
WTDKUACNrectangle and square shapes - [178]
T7SJSJIHtest: undo naming a point - [179]
KICO5EE5typos - [180]
WLWNS6FBa bug I've never run into - [181]
BYG5CEMVsupport for naming points - [182]
ZTMRQZSWReadme - [183]
AYE2VEGJextract a couple of methods - [184]
VHQCNMARseveral more modules - [185]
NUCZBE77bugfix: alignment of help screen - [186]
VXORMHMEdelete experimental REPL - [187]
7CLGG7J2test: autosave after any shape - [188]
2L5MEZV3experiment: new edit namespace - [189]
U7M4M2F7bugfix: don't rely on Screen_bottom1 while scrolling - [190]
BW2IUB3Kkeep all text cache writes inside text.lua - [191]
2LOQ5ALJadd args to some functions - [192]
73OCE2MCafter much struggle, a brute-force undo - [193]
6DE7RBZ6move mouse_released events to Drawing - [194]
3OKKTUT4up and down arrow now moving by screen line where possible - [195]
LAW2O3NWextract variable Margin_left - [196]
52ZZ5TIEswitch to line index in a function - [197]
T57DTBX6add args to some functions - [198]
Y4VYNEGFtest: autosave after name/move/delete of point - [199]
PWHZPJJMalways show current filename in window title - [200]
HGC5RGJPswitch to line index in a function - [201]
F63Q4OV7several bugfixes - [202]
TRNWIQN6more precise height calculation when scrolling up as much as possible while keeping cursor on screen - [203]
EHSUSZMKmore idiomatic variable names - [204]
62PZGSUCoptimization: moving cursor to next word - [205]
Z4KNS42Nto open a file without a terminal, drag it on! - [206]
U2TKUOIDbugfix: undo drawing creation - [207]
LXTTOB33extract a couple of files - [208]
LSYLEVBDdrop some redundant args when clearing the cache - [209]
EETIR4GXbugfix: skip over drawings when searching - [210]
WSXSEZQ2switch circles to 'o' to avoid conflicting with copy - [211]
CRYGI3LRmore drawing tests - [212]
Z5HLXU4Padd state arg to a few functions - [213]
RT6EV6OPdelegate update events to drawings - [214]
KQWIMWJ5deemphasize the terminal in Readme - [215]
7SFHSB47rename - [216]
B4YZWV6Sbugfix: checking if a point is on a manhattan line - [217]
YTSPVDZHfirst successful pagedown test, first bug found by test - [218]
7DYUAOI6test: undo moving point - [219]
NVSWVPW5move - [220]
WPW3AVFSmore precise shape selection - [221]
SQLVYKVJrename - [222]
4AXV2HG4all pending manual tests done! - [223]
KAUD3YIKtests: deleting points/shapes - [224]
IDGP4BJZnew known issue with drawings - [225]
BULPIBEGbeginnings of a module for the text editor - [226]
OMTGHWMAyet another bugfix. But for how long? - [227]
W2CQ7YNGmore chunks, same approach - [228]
VSBSWTE4bugfix: where cursor is drawn - [229]
LLAOOMULbugfix: search upwards - [230]
PYGMASTVdisable some debug prints - [231]
V3EABA35skip multiple consecutive whitespace - [232]
FZCKGO2Imake local functions look different - [233]
HOSPP2ANcrisp font rendering - [234]
VG75U7IMbugfix: typing should delete highlighted text - [235]
ERQKFTPVextract method - [236]
AVFRVNFRbetter handle moving points - [237]
P5QNVXSNdrop final mention of state global beyond main.lua - [238]
DXT4QTAHa few more integer coordinates - [239]
NYQ7HD4Dmove - [240]
K2X6G75Zstart writing some tests for drawings - [241]
PX3736DXbetter error message - [242]
MTJEVRJRadd state arg to a few functions - [243]
PJEQCTBLadd state arg to Drawing.update - [244]
BU3LUPY3bugfix in help - [245]
GSPXUEQO. - [246]
DLQAEAC7add state arg to Drawing.mouse_pressed - [247]
LF7BWEG4group all editor globals - [248]
4J2L6JMRbugfix: deleting a selection spanning pages - [249]
4WAFGF4Zselection bugfix - [250]
2MA33THZfew more transitions between shapes - [251]
VFJEVPPObugfix: function names - [252]
O7QH4N4Wspeeding up copy, attempt 1 - [253]
2XLZCWZCbugfix: rectangles and squares are now saved - [254]
MDXGMZU2disable all debug prints - [255]
CTJ3IZGSadd args to some functions - [256]
VDJSUX2Qtypos - [257]
PX7DDEMOautosave slightly less aggressively - [258]
IWYLK45Kclicking to the right of a line within line width - [259]
S2MISTTMadd state arg to a few functions - [260]
4YDBYBA4clean up memory leak experiments - [261]
F65ADDGLadd state arg to a few functions - [262]
MLG2OGU7things seem to feel snappier now - [263]
D2GCFTTTclean up repl functionality - [264]
FZBXBUFFbugfix: search - [265]
ESETRNLBbugfix: printing the first part of a line at the bottom made it seem non-wrapping - [266]
CVSRHMJ2experiment: slightly adaptive scrolling - [267]
2TQR4PSYadd args to some functions - [268]
KECEMMMRextract couple of functions - [269]
MGOQ5XAVstart uppercasing globals - [270]
CBPV5SSIstop handling nil screen_line_starting_pos everywhere - [271]
GSV7DABCmake online help fit within a drawing - [272]
4VKEE43Zbugfix - [273]
TO6Y2G3Umore decoupling editor tests from App - [274]
PLKNHYZ4extract a function - [275]
BPWFKBXTnew test: dragging and dropping a file on lines.love - [276]
X3F7ECSLadd state arg to some functions - [277]
GN3IF4WFbugfix: pasting newlines - [278]
CNCYMM6Amake test initializations a little more obvious - [279]
PK5U572Cdrop some extra args - [280]
P66MRF3Ubugfix: don't append metadata when it already exists - [281]
3GFQP6IRstop saving the entire file when modifying drawings - [282]
OTIBCAUJlove2d scaffold - [283]
HTWAM4NZbugfix: scrolling in left/right movements - [284]
OP643FFGmove - [285]
LNUHQOGHstart passing in Editor_state explicitly - [286]
MSOQI3A5bugfix: check before cursor on same line - [287]
CZRMAMSBclearer discription of how to run lines.love - [288]
2JLVAYHBstart decoupling editor tests from App - [289]
AVTNUQYRbasic test-enabled framework - [290]
FHSZYAZ2more precise search highlighting - [291]
2LC3BM2Nsupport other whitespace chars in word movements
Change contents
- file deletion: drawing_tests.lua
-- major tests for drawings-- We minimize assumptions about specific pixels, and try to test at the level-- of specific shapes. In particular, no tests of freehand drawings.function test_draw_line()io.write('\ntest_draw_line')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(#Editor_state.lines, 2, 'F - test_draw_line/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_line/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_line/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_line/baseline/#shapes')-- draw a linelocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_draw_line/#shapes')check_eq(#drawing.points, 2, 'F - test_draw_line/#points')check_eq(drawing.shapes[1].mode, 'line', 'F - test_draw_line/shape:1')local p1 = drawing.points[drawing.shapes[1].p1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p1.x, 5, 'F - test_draw_line/p1:x')check_eq(p1.y, 6, 'F - test_draw_line/p1:y')check_eq(p2.x, 35, 'F - test_draw_line/p2:x')check_eq(p2.y, 36, 'F - test_draw_line/p2:y')endfunction test_draw_arc()io.write('\ntest_draw_arc')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(#Editor_state.lines, 2, 'F - test_draw_arc/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_arc/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_arc/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_arc/baseline/#shapes')-- draw an arclocal drawing = Editor_state.lines[1]edit.run_after_keychord(Editor_state, 'a') -- arc modeedit.run_after_mouse_release(Editor_state, Editor_state.left+35+50, Editor_state.top+Drawing_padding_top+36+50, 1) -- 45°check_eq(#drawing.shapes, 1, 'F - test_draw_arc/#shapes')check_eq(drawing.shapes[1].mode, 'arc', 'F - test_draw_horizontal_line/shape_mode')local arc = drawing.shapes[1]check_eq(arc.radius, 30, 'F - test_draw_arc/radius')local center = drawing.points[arc.center]check_eq(center.x, 35, 'F - test_draw_arc/center:x')check_eq(center.y, 36, 'F - test_draw_arc/center:y')check_eq(arc.start_angle, 0, 'F - test_draw_arc/start:angle')check_eq(arc.end_angle, math.pi/4, 'F - test_draw_arc/end:angle')endfunction test_draw_polygon()io.write('\ntest_draw_polygon')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_polygon/baseline/drawing_mode')check_eq(#Editor_state.lines, 2, 'F - test_draw_polygon/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_polygon/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_polygon/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_polygon/baseline/#shapes')-- first pointedit.run_after_keychord(Editor_state, 'g') -- polygon mode-- second pointedit.run_after_keychord(Editor_state, 'p') -- add point-- final pointlocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_draw_polygon/#shapes')check_eq(#drawing.points, 3, 'F - test_draw_polygon/vertices')local shape = drawing.shapes[1]check_eq(shape.mode, 'polygon', 'F - test_draw_polygon/shape_mode')check_eq(#shape.vertices, 3, 'F - test_draw_polygon/vertices')local p = drawing.points[shape.vertices[1]]check_eq(p.x, 5, 'F - test_draw_polygon/p1:x')check_eq(p.y, 6, 'F - test_draw_polygon/p1:y')local p = drawing.points[shape.vertices[2]]check_eq(p.x, 65, 'F - test_draw_polygon/p2:x')check_eq(p.y, 36, 'F - test_draw_polygon/p2:y')local p = drawing.points[shape.vertices[3]]check_eq(p.x, 35, 'F - test_draw_polygon/p3:x')check_eq(p.y, 26, 'F - test_draw_polygon/p3:y')endfunction test_draw_rectangle()io.write('\ntest_draw_rectangle')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_rectangle/baseline/drawing_mode')check_eq(#Editor_state.lines, 2, 'F - test_draw_rectangle/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_rectangle/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_rectangle/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_rectangle/baseline/#shapes')-- first pointedit.run_after_keychord(Editor_state, 'r') -- rectangle mode-- second point/first edgeedit.run_after_keychord(Editor_state, 'p')-- override second point/first edgeedit.run_after_keychord(Editor_state, 'p')-- release (decides 'thickness' of rectangle perpendicular to first edge)local drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_draw_rectangle/#shapes')check_eq(#drawing.points, 5, 'F - test_draw_rectangle/#points') -- currently includes every point addedlocal shape = drawing.shapes[1]check_eq(shape.mode, 'rectangle', 'F - test_draw_rectangle/shape_mode')check_eq(#shape.vertices, 4, 'F - test_draw_rectangle/vertices')local p = drawing.points[shape.vertices[1]]check_eq(p.x, 35, 'F - test_draw_rectangle/p1:x')check_eq(p.y, 36, 'F - test_draw_rectangle/p1:y')local p = drawing.points[shape.vertices[2]]check_eq(p.x, 75, 'F - test_draw_rectangle/p2:x')check_eq(p.y, 76, 'F - test_draw_rectangle/p2:y')local p = drawing.points[shape.vertices[3]]check_eq(p.x, 70, 'F - test_draw_rectangle/p3:x')check_eq(p.y, 81, 'F - test_draw_rectangle/p3:y')local p = drawing.points[shape.vertices[4]]check_eq(p.x, 30, 'F - test_draw_rectangle/p4:x')check_eq(p.y, 41, 'F - test_draw_rectangle/p4:y')endfunction test_draw_rectangle_intermediate()io.write('\ntest_draw_rectangle_intermediate')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_rectangle_intermediate/baseline/drawing_mode')check_eq(#Editor_state.lines, 2, 'F - test_draw_rectangle_intermediate/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_rectangle_intermediate/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_rectangle_intermediate/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_rectangle_intermediate/baseline/#shapes')-- first pointedit.run_after_keychord(Editor_state, 'r') -- rectangle mode-- second point/first edgeedit.run_after_keychord(Editor_state, 'p')-- override second point/first edgelocal drawing = Editor_state.lines[1]edit.run_after_keychord(Editor_state, 'p')check_eq(#drawing.points, 3, 'F - test_draw_rectangle_intermediate/#points') -- currently includes every point addedlocal pending = drawing.pendingcheck_eq(pending.mode, 'rectangle', 'F - test_draw_rectangle_intermediate/shape_mode')check_eq(#pending.vertices, 2, 'F - test_draw_rectangle_intermediate/vertices')local p = drawing.points[pending.vertices[1]]check_eq(p.x, 35, 'F - test_draw_rectangle_intermediate/p1:x')check_eq(p.y, 36, 'F - test_draw_rectangle_intermediate/p1:y')local p = drawing.points[pending.vertices[2]]check_eq(p.x, 75, 'F - test_draw_rectangle_intermediate/p2:x')check_eq(p.y, 76, 'F - test_draw_rectangle_intermediate/p2:y')-- outline of rectangle is drawn based on where the mouse is, but we can't check that so farendfunction test_draw_square()io.write('\ntest_draw_square')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_draw_square/baseline/drawing_mode')check_eq(#Editor_state.lines, 2, 'F - test_draw_square/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_square/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_square/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_square/baseline/#shapes')-- first pointedit.run_after_keychord(Editor_state, 's') -- square mode-- second point/first edgeedit.run_after_keychord(Editor_state, 'p')-- override second point/first edgeedit.run_after_keychord(Editor_state, 'p')-- release (decides which side of first edge to draw square on)local drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_draw_square/#shapes')check_eq(#drawing.points, 5, 'F - test_draw_square/#points') -- currently includes every point addedcheck_eq(drawing.shapes[1].mode, 'square', 'F - test_draw_square/shape_mode')check_eq(#drawing.shapes[1].vertices, 4, 'F - test_draw_square/vertices')local p = drawing.points[drawing.shapes[1].vertices[1]]check_eq(p.x, 35, 'F - test_draw_square/p1:x')check_eq(p.y, 36, 'F - test_draw_square/p1:y')local p = drawing.points[drawing.shapes[1].vertices[2]]check_eq(p.x, 65, 'F - test_draw_square/p2:x')check_eq(p.y, 66, 'F - test_draw_square/p2:y')local p = drawing.points[drawing.shapes[1].vertices[3]]check_eq(p.x, 35, 'F - test_draw_square/p3:x')check_eq(p.y, 96, 'F - test_draw_square/p3:y')local p = drawing.points[drawing.shapes[1].vertices[4]]check_eq(p.x, 5, 'F - test_draw_square/p4:x')check_eq(p.y, 66, 'F - test_draw_square/p4:y')endfunction test_name_point()io.write('\ntest_name_point')-- create a drawing with a lineEditor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'-- draw a linelocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_name_point/baseline/#shapes')check_eq(#drawing.points, 2, 'F - test_name_point/baseline/#points')check_eq(drawing.shapes[1].mode, 'line', 'F - test_name_point/baseline/shape:1')local p1 = drawing.points[drawing.shapes[1].p1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p1.x, 5, 'F - test_name_point/baseline/p1:x')check_eq(p1.y, 6, 'F - test_name_point/baseline/p1:y')check_eq(p2.x, 35, 'F - test_name_point/baseline/p2:x')check_eq(p2.y, 36, 'F - test_name_point/baseline/p2:y')check_nil(p2.name, 'F - test_name_point/baseline/p2:name')-- enter 'name' mode without moving the mousecheck_eq(Editor_state.current_drawing_mode, 'name', 'F - test_name_point/mode:1')check_eq(p2.name, 'A', 'F - test_name_point')-- still in 'name' mode-- exit 'name' modecheck_eq(Editor_state.current_drawing_mode, 'line', 'F - test_name_point/mode:3')check_eq(p2.name, 'A', 'F - test_name_point')endfunction test_delete_point_from_polygon()io.write('\ntest_delete_point_from_polygon')-- create a drawing with two lines connected at a pointEditor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)-- first pointedit.run_after_keychord(Editor_state, 'g') -- polygon mode-- second pointedit.run_after_keychord(Editor_state, 'p') -- add point-- third pointlocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_delete_point_from_polygon/baseline/#shapes')check_eq(drawing.shapes[1].mode, 'polygon', 'F - test_delete_point_from_polygon/baseline/mode')check_eq(#drawing.shapes[1].vertices, 3, 'F - test_delete_point_from_polygon/baseline/vertices')-- hover on a point and deleteedit.run_after_keychord(Editor_state, 'C-d')-- there's < 3 points left, so the whole polygon is deletedcheck_eq(drawing.shapes[1].mode, 'deleted', 'F - test_delete_point_from_polygon')endfunction test_undo_name_point()io.write('\ntest_undo_name_point')-- create a drawing with a lineEditor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'-- draw a linelocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_undo_name_point/baseline/#shapes')check_eq(#drawing.points, 2, 'F - test_undo_name_point/baseline/#points')check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_name_point/baseline/shape:1')local p1 = drawing.points[drawing.shapes[1].p1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p1.x, 5, 'F - test_undo_name_point/baseline/p1:x')check_eq(p1.y, 6, 'F - test_undo_name_point/baseline/p1:y')check_eq(p2.x, 35, 'F - test_undo_name_point/baseline/p2:x')check_eq(p2.y, 36, 'F - test_undo_name_point/baseline/p2:y')check_nil(p2.name, 'F - test_undo_name_point/baseline/p2:name')-- enter 'name' mode without moving the mousecheck_eq(p2.name, 'A', 'F - test_undo_name_point/baseline')-- undolocal drawing = Editor_state.lines[1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p2.name, '', 'F - test_undo_name_point') -- not quite what it was before, but close enoughcheck_eq(p2.name, '', 'F - test_undo_name_point/save')endfunction test_undo_move_point()io.write('\ntest_undo_move_point')-- create a drawing with a linelocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_undo_move_point/baseline/#shapes')check_eq(#drawing.points, 2, 'F - test_undo_move_point/baseline/#points')check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_move_point/baseline/shape:1')local p1 = drawing.points[drawing.shapes[1].p1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p1.x, 5, 'F - test_undo_move_point/baseline/p1:x')check_eq(p1.y, 6, 'F - test_undo_move_point/baseline/p1:y')check_eq(p2.x, 35, 'F - test_undo_move_point/baseline/p2:x')check_eq(p2.y, 36, 'F - test_undo_move_point/baseline/p2:y')check_nil(p2.name, 'F - test_undo_move_point/baseline/p2:name')-- move p2local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p2.x, 26, 'F - test_undo_move_point/x')check_eq(p2.y, 44, 'F - test_undo_move_point/y')-- exit 'move' modecheck_eq(Editor_state.next_history, 4, 'F - test_undo_move_point/next_history')-- undolocal drawing = Editor_state.lines[1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p2.x, 35, 'F - test_undo_move_point/x')check_eq(p2.y, 36, 'F - test_undo_move_point/y')check_eq(p2.x, 35, 'F - test_undo_move_point/save/x')check_eq(p2.y, 36, 'F - test_undo_move_point/save/y')endendfunction test_undo_delete_point()io.write('\ntest_undo_delete_point')-- create a drawing with two lines connected at a pointlocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 2, 'F - test_undo_delete_point/baseline/#shapes')check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_delete_point/baseline/shape:1')check_eq(drawing.shapes[2].mode, 'line', 'F - test_undo_delete_point/baseline/shape:2')-- hover on the common point and deleteedit.run_after_keychord(Editor_state, 'C-d')check_eq(drawing.shapes[1].mode, 'deleted', 'F - test_undo_delete_point/shape:1')check_eq(drawing.shapes[2].mode, 'deleted', 'F - test_undo_delete_point/shape:2')-- undolocal drawing = Editor_state.lines[1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(drawing.shapes[1].mode, 'line', 'F - test_undo_delete_point/shape:1')check_eq(drawing.shapes[2].mode, 'line', 'F - test_undo_delete_point/shape:2')-- undo is savedText.redraw_all(Editor_state)check_eq(#Editor_state.lines[1].shapes, 2, 'F - test_undo_delete_point/save')load_from_disk(Editor_state)-- wait until saveApp.wait_fake_time(3.1)edit.update(Editor_state, 0)check_eq(Editor_state.next_history, 3, 'F - test_undo_move_point/next_history')edit.run_after_keychord(Editor_state, 'C-z')App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+36)Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'-- wait until saveApp.wait_fake_time(3.1)edit.update(Editor_state, 0)-- undo is savedText.redraw_all(Editor_state)local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]load_from_disk(Editor_state)check_eq(Editor_state.next_history, 2, 'F - test_undo_move_point/next_history')edit.run_after_keychord(Editor_state, 'C-z')edit.run_after_keychord(Editor_state, 'C-z') -- bug: need to undo twiceedit.run_after_mouse_click(Editor_state, Editor_state.left+26, Editor_state.top+Drawing_padding_top+44, 1)edit.run_after_keychord(Editor_state, 'C-u')edit.update(Editor_state, 0.05)App.mouse_move(Editor_state.left+26, Editor_state.top+Drawing_padding_top+44)Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'-- wait until saveApp.wait_fake_time(3.1)edit.update(Editor_state, 0)-- undo is savedText.redraw_all(Editor_state)local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]load_from_disk(Editor_state)check_eq(Editor_state.next_history, 3, 'F - test_undo_name_point/next_history')edit.run_after_keychord(Editor_state, 'C-z')check_eq(#Editor_state.history, 3, 'F - test_undo_name_point/baseline/history:2')check_eq(Editor_state.next_history, 4, 'F - test_undo_name_point/baseline/next_history')--? print('b', Editor_state.lines.current_drawing)edit.run_after_keychord(Editor_state, 'C-n')edit.run_after_textinput(Editor_state, 'A')edit.run_after_keychord(Editor_state, 'return')check_eq(#Editor_state.history, 1, 'F - test_undo_name_point/baseline/history:1')--? print('a', Editor_state.lines.current_drawing)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)edit.run_after_mouse_release(Editor_state, Editor_state.left+14, Editor_state.top+Drawing_padding_top+16, 1)App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()-- wait until saveApp.wait_fake_time(3.1)edit.update(Editor_state, 0)-- change is savedText.redraw_all(Editor_state)local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]check_eq(p2.name, 'A', 'F - test_name_point/save')load_from_disk(Editor_state)endfunction test_move_point()io.write('\ntest_move_point')-- create a drawing with a linelocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_move_point/baseline/#shapes')check_eq(#drawing.points, 2, 'F - test_move_point/baseline/#points')check_eq(drawing.shapes[1].mode, 'line', 'F - test_move_point/baseline/shape:1')local p1 = drawing.points[drawing.shapes[1].p1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p1.x, 5, 'F - test_move_point/baseline/p1:x')check_eq(p1.y, 6, 'F - test_move_point/baseline/p1:y')check_eq(p2.x, 35, 'F - test_move_point/baseline/p2:x')check_eq(p2.y, 36, 'F - test_move_point/baseline/p2:y')-- wait until saveApp.wait_fake_time(3.1)-- line is saved to diskText.redraw_all(Editor_state)local drawing = Editor_state.lines[1]local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]check_eq(p2.x, 35, 'F - test_move_point/save/x')check_eq(p2.y, 36, 'F - test_move_point/save/y')-- enter 'move' mode without moving the mousecheck_eq(Editor_state.current_drawing_mode, 'move', 'F - test_move_point/mode:1')-- point is liftedcheck_eq(drawing.pending.mode, 'move', 'F - test_move_point/mode:2')check_eq(drawing.pending.target_point, p2, 'F - test_move_point/target')-- move pointlocal p2 = drawing.points[drawing.shapes[1].p2]check_eq(p2.x, 26, 'F - test_move_point/x')check_eq(p2.y, 44, 'F - test_move_point/y')-- exit 'move' modecheck_eq(Editor_state.current_drawing_mode, 'line', 'F - test_move_point/mode:3')check_eq(drawing.pending, {}, 'F - test_move_point/pending')-- wait until saveApp.wait_fake_time(3.1)edit.update(Editor_state, 0)-- change is savedText.redraw_all(Editor_state)local p2 = Editor_state.lines[1].points[drawing.shapes[1].p2]check_eq(p2.x, 26, 'F - test_move_point/save/x')check_eq(p2.y, 44, 'F - test_move_point/save/y')load_from_disk(Editor_state)endfunction test_delete_lines_at_point()io.write('\ntest_delete_lines_at_point')-- create a drawing with two lines connected at a pointlocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 2, 'F - test_delete_lines_at_point/baseline/#shapes')check_eq(drawing.shapes[1].mode, 'line', 'F - test_delete_lines_at_point/baseline/shape:1')check_eq(drawing.shapes[2].mode, 'line', 'F - test_delete_lines_at_point/baseline/shape:2')-- hover on the common point and deleteedit.run_after_keychord(Editor_state, 'C-d')check_eq(drawing.shapes[1].mode, 'deleted', 'F - test_delete_lines_at_point/shape:1')check_eq(drawing.shapes[2].mode, 'deleted', 'F - test_delete_lines_at_point/shape:2')endfunction test_delete_line_under_mouse_pointer()io.write('\ntest_delete_line_under_mouse_pointer')-- create a drawing with two lines connected at a pointlocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 2, 'F - test_delete_line_under_mouse_pointer/baseline/#shapes')check_eq(drawing.shapes[1].mode, 'line', 'F - test_delete_line_under_mouse_pointer/baseline/shape:1')check_eq(drawing.shapes[2].mode, 'line', 'F - test_delete_line_under_mouse_pointer/baseline/shape:2')-- hover on one of the lines and deleteedit.run_after_keychord(Editor_state, 'C-d')-- only that line is deletedcheck_eq(drawing.shapes[1].mode, 'deleted', 'F - test_delete_line_under_mouse_pointer/shape:1')check_eq(drawing.shapes[2].mode, 'line', 'F - test_delete_line_under_mouse_pointer/shape:2')endfunction test_delete_point_from_polygon()io.write('\ntest_delete_point_from_polygon')-- create a drawing with two lines connected at a pointEditor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)-- first pointedit.run_after_keychord(Editor_state, 'g') -- polygon mode-- second pointedit.run_after_keychord(Editor_state, 'p') -- add point-- third pointedit.run_after_keychord(Editor_state, 'p') -- add point-- fourth pointlocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_delete_point_from_polygon/baseline/#shapes')check_eq(drawing.shapes[1].mode, 'polygon', 'F - test_delete_point_from_polygon/baseline/mode')check_eq(#drawing.shapes[1].vertices, 4, 'F - test_delete_point_from_polygon/baseline/vertices')-- hover on a point and deleteedit.run_after_keychord(Editor_state, 'C-d')-- just the one point is deletedcheck_eq(drawing.shapes[1].mode, 'polygon', 'F - test_delete_point_from_polygon/shape')check_eq(#drawing.shapes[1].vertices, 3, 'F - test_delete_point_from_polygon/vertices')App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+26)edit.run_after_mouse_release(Editor_state, Editor_state.left+14, Editor_state.top+Drawing_padding_top+16, 1)App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+26)App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()App.mouse_move(Editor_state.left+25, Editor_state.top+Drawing_padding_top+26)Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()-- wait for some timeApp.wait_fake_time(3.1)edit.update(Editor_state, 0)-- deleted points disappear after file is reloadedText.redraw_all(Editor_state)check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_delete_lines_at_point/save')load_from_disk(Editor_state)App.mouse_move(Editor_state.left+35, Editor_state.top+Drawing_padding_top+36)Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+55, Editor_state.top+Drawing_padding_top+26, 1)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'function test_move_point_on_manhattan_line()io.write('\ntest_move_point_on_manhattan_line')-- create a drawing with a manhattan linelocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_move_point_on_manhattan_line/baseline/#shapes')check_eq(#drawing.points, 2, 'F - test_move_point_on_manhattan_line/baseline/#points')check_eq(drawing.shapes[1].mode, 'manhattan', 'F - test_move_point_on_manhattan_line/baseline/shape:1')-- enter 'move' modecheck_eq(Editor_state.current_drawing_mode, 'move', 'F - test_move_point_on_manhattan_line/mode:1')-- move point-- line is no longer manhattancheck_eq(drawing.shapes[1].mode, 'line', 'F - test_move_point_on_manhattan_line/baseline/shape:1')endedit.update(Editor_state, 0.05)App.mouse_move(Editor_state.left+26, Editor_state.top+Drawing_padding_top+44)edit.run_after_keychord(Editor_state, 'C-u')edit.draw(Editor_state)Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'manhattan'edit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+46, 1)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'edit.run_after_mouse_click(Editor_state, Editor_state.left+26, Editor_state.top+Drawing_padding_top+44, 1)edit.update(Editor_state, 0.05)App.mouse_move(Editor_state.left+26, Editor_state.top+Drawing_padding_top+44)edit.run_after_keychord(Editor_state, 'C-u')edit.draw(Editor_state)load_from_disk(Editor_state)edit.update(Editor_state, 0)Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'edit.run_after_keychord(Editor_state, 'return')check_eq(Editor_state.current_drawing_mode, 'name', 'F - test_name_point/mode:2')edit.run_after_textinput(Editor_state, 'A')edit.run_after_keychord(Editor_state, 'C-n')edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+15, Editor_state.top+Drawing_padding_top+26, 1)App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+66)App.mouse_move(Editor_state.left+42, Editor_state.top+Drawing_padding_top+45)edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_square/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()App.mouse_move(Editor_state.left+75, Editor_state.top+Drawing_padding_top+76)App.mouse_move(Editor_state.left+42, Editor_state.top+Drawing_padding_top+45)edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_rectangle_intermediate/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()edit.run_after_mouse_release(Editor_state, Editor_state.left+15, Editor_state.top+Drawing_padding_top+26, 1)App.mouse_move(Editor_state.left+75, Editor_state.top+Drawing_padding_top+76)App.mouse_move(Editor_state.left+42, Editor_state.top+Drawing_padding_top+45)edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_rectangle/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+26, 1)App.mouse_move(Editor_state.left+65, Editor_state.top+Drawing_padding_top+36)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_polygon/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()check_eq(#drawing.points, 1, 'F - test_draw_arc/#points')edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)App.mouse_move(Editor_state.left+35+30, Editor_state.top+Drawing_padding_top+36)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_arc/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'circle'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()-- wait until saveApp.wait_fake_time(3.1)edit.update(Editor_state, 0)-- The format on disk isn't perfectly stable. Table fields can be reordered.-- So just reload from disk to verify.Text.redraw_all(Editor_state)local drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_draw_line/save/#shapes')check_eq(#drawing.points, 2, 'F - test_draw_line/save/#points')check_eq(drawing.shapes[1].mode, 'line', 'F - test_draw_line/save/shape:1')local p1 = drawing.points[drawing.shapes[1].p1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p1.x, 5, 'F - test_draw_line/save/p1:x')check_eq(p1.y, 6, 'F - test_draw_line/save/p1:y')check_eq(p2.x, 35, 'F - test_draw_line/save/p2:x')check_eq(p2.y, 36, 'F - test_draw_line/save/p2:y')load_from_disk(Editor_state)endfunction test_draw_horizontal_line()io.write('\ntest_draw_horizontal_line')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(#Editor_state.lines, 2, 'F - test_draw_horizontal_line/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_horizontal_line/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_horizontal_line/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_horizontal_line/baseline/#shapes')-- draw a line that is more horizontal than verticallocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 1, 'F - test_draw_horizontal_line/#shapes')check_eq(#drawing.points, 2, 'F - test_draw_horizontal_line/#points')check_eq(drawing.shapes[1].mode, 'manhattan', 'F - test_draw_horizontal_line/shape_mode')local p1 = drawing.points[drawing.shapes[1].p1]local p2 = drawing.points[drawing.shapes[1].p2]check_eq(p1.x, 5, 'F - test_draw_horizontal_line/p1:x')check_eq(p1.y, 6, 'F - test_draw_horizontal_line/p1:y')check_eq(p2.x, 35, 'F - test_draw_horizontal_line/p2:x')check_eq(p2.y, p1.y, 'F - test_draw_horizontal_line/p2:y')endfunction test_draw_circle()io.write('\ntest_draw_circle')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(#Editor_state.lines, 2, 'F - test_draw_circle/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_circle/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_circle/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_circle/baseline/#shapes')-- draw a circlelocal drawing = Editor_state.lines[1]edit.run_after_keychord(Editor_state, 'C-o')edit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35+30, Editor_state.top+Drawing_padding_top+36, 1)check_eq(#drawing.shapes, 1, 'F - test_draw_circle/#shapes')check_eq(#drawing.points, 1, 'F - test_draw_circle/#points')check_eq(drawing.shapes[1].mode, 'circle', 'F - test_draw_horizontal_line/shape_mode')check_eq(drawing.shapes[1].radius, 30, 'F - test_draw_circle/radius')local center = drawing.points[drawing.shapes[1].center]check_eq(center.x, 35, 'F - test_draw_circle/center:x')check_eq(center.y, 36, 'F - test_draw_circle/center:y')endfunction test_keys_do_not_affect_shape_when_mouse_up()io.write('\ntest_keys_do_not_affect_shape_when_mouse_up')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)-- hover over drawing and press 'o' without holding mouseedit.run_after_keychord(Editor_state, 'o')-- no change to drawing mode-- no change to text either because we didn't run the textinput eventendfunction test_draw_circle_mid_stroke()io.write('\ntest_draw_circle_mid_stroke')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(#Editor_state.lines, 2, 'F - test_draw_circle_mid_stroke/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_draw_circle_mid_stroke/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_draw_circle_mid_stroke/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_draw_circle_mid_stroke/baseline/#shapes')-- draw a circlelocal drawing = Editor_state.lines[1]edit.run_after_keychord(Editor_state, 'o')edit.run_after_mouse_release(Editor_state, Editor_state.left+35+30, Editor_state.top+Drawing_padding_top+36, 1)check_eq(#drawing.shapes, 1, 'F - test_draw_circle_mid_stroke/#shapes')check_eq(#drawing.points, 1, 'F - test_draw_circle_mid_stroke/#points')check_eq(drawing.shapes[1].mode, 'circle', 'F - test_draw_horizontal_line/shape_mode')check_eq(drawing.shapes[1].radius, 30, 'F - test_draw_circle_mid_stroke/radius')local center = drawing.points[drawing.shapes[1].center]check_eq(center.x, 35, 'F - test_draw_circle_mid_stroke/center:x')check_eq(center.y, 36, 'F - test_draw_circle_mid_stroke/center:y')App.mouse_move(Editor_state.left+4, Editor_state.top+Drawing_padding_top+4) -- hover on drawingedit.run_after_mouse_press(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_circle_mid_stroke/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()check_eq(Editor_state.current_drawing_mode, 'line', 'F - test_keys_do_not_affect_shape_when_mouse_up/drawing_mode')App.mouse_move(Editor_state.left+4, Editor_state.top+Drawing_padding_top+4) -- hover on drawingApp.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()endfunction test_cancel_stroke()io.write('\ntest_cancel_stroke')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)check_eq(#Editor_state.lines, 2, 'F - test_cancel_stroke/baseline/#lines')check_eq(Editor_state.lines[1].mode, 'drawing', 'F - test_cancel_stroke/baseline/mode')check_eq(Editor_state.lines[1].h, 128, 'F - test_cancel_stroke/baseline/y')check_eq(#Editor_state.lines[1].shapes, 0, 'F - test_cancel_stroke/baseline/#shapes')-- start drawing a line-- cancellocal drawing = Editor_state.lines[1]check_eq(#drawing.shapes, 0, 'F - test_cancel_stroke/#shapes')edit.run_after_keychord(Editor_state, 'escape')edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_cancel_stroke/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'App.mouse_move(Editor_state.left+4, Editor_state.top+Drawing_padding_top+4) -- hover on drawingcheck_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_circle/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+26, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_horizontal_line/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'manhattan'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()edit.run_after_mouse_press(Editor_state, Editor_state.left+5, Editor_state.top+Drawing_padding_top+6, 1)edit.run_after_mouse_release(Editor_state, Editor_state.left+35, Editor_state.top+Drawing_padding_top+36, 1)check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'F - test_draw_line/baseline/y')Editor_state.lines = load_array{'```lines', '```', ''}Editor_state.current_drawing_mode = 'line'edit.draw(Editor_state)Text.redraw_all(Editor_state)App.screen.init{width=Test_margin_left+256, height=300} -- drawing coordinates 1:1 with pixelsEditor_state = edit.initialize_test_state()Editor_state.filename = 'foo'function test_creating_drawing_saves()io.write('\ntest_creating_drawing_saves')App.screen.init{width=120, height=60}-- click on button to create drawing-- file not immediately savedcheck_nil(App.filesystem['foo'], 'F - test_creating_drawing_saves/early')-- wait until saveApp.wait_fake_time(3.1)-- filesystem contains drawing and an empty line of textcheck_eq(App.filesystem['foo'], '```lines\n```\n\n', 'F - test_creating_drawing_saves')endedit.update(Editor_state, 0)edit.update(Editor_state, 0.01)edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)Editor_state.filename = 'foo'Editor_state.lines = load_array{}edit.draw(Editor_state)Text.redraw_all(Editor_state)Editor_state = edit.initialize_test_state() - file deletion: drawing.lua
-- primitives for editing drawingsDrawing = {}require 'drawing_tests'elseendreturnendendif line.show_help thenreturnendfor _,shape in ipairs(line.shapes) doassert(shape)elseendendif p.deleted == nil thenelseendif p.name thenlove.graphics.print(p.name, x,y)if State.current_drawing_mode == 'name' and i == line.pending.target_point then-- create a faint red box for the namelocal name_textif p.name == '' thenelseendendlove.graphics.rectangle('fill', x,y, App.width(name_text), State.line_height)name_text = App.newText(love.graphics.getFont(), p.name)name_text = State.em-- TODO: avoid computing name width on every repaintApp.color(Current_name_background_color)-- TODO: cliplocal x,y = px(p.x)+5, py(p.y)+5endendendendendif shape.mode == 'freehand' thenlocal prev = nilfor _,point in ipairs(shape.points) doif prev thenendprev = pointendelseif shape.mode == 'line' or shape.mode == 'manhattan' thenlocal p1 = drawing.points[shape.p1]local p2 = drawing.points[shape.p2]elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' thenlocal prev = nilfor _,point in ipairs(shape.vertices) dolocal curr = drawing.points[point]if prev thenendprev = currend-- close the looplocal curr = drawing.points[shape.vertices[1]]elseif shape.mode == 'circle' thenlocal center = drawing.points[shape.center]elseif shape.mode == 'arc' thenlocal center = drawing.points[shape.center]elseif shape.mode == 'deleted' then-- ignoreelseprint(shape.mode)assert(false)endendlocal shape = drawing.pendingelseif shape.mode == 'line' thenif mx < 0 or mx >= 256 or my < 0 or my >= drawing.h thenreturnendlocal p1 = drawing.points[shape.p1]elseif shape.mode == 'manhattan' thenif mx < 0 or mx >= 256 or my < 0 or my >= drawing.h thenreturnendif math.abs(mx-p1.x) > math.abs(my-p1.y) thenelseendelseif shape.mode == 'polygon' then-- don't close the loop on a pending polygonlocal prev = nilfor _,point in ipairs(shape.vertices) dolocal curr = drawing.points[point]if prev thenendprev = currendelseif shape.mode == 'rectangle' thenlocal first = drawing.points[shape.vertices[1]]if #shape.vertices == 1 thenreturnendlocal second = drawing.points[shape.vertices[2]]local thirdx,thirdy, fourthx,fourthy = Drawing.complete_rectangle(first.x,first.y, second.x,second.y, mx,my)elseif shape.mode == 'square' thenlocal first = drawing.points[shape.vertices[1]]if #shape.vertices == 1 thenreturnendlocal second = drawing.points[shape.vertices[2]]local thirdx,thirdy, fourthx,fourthy = Drawing.complete_square(first.x,first.y, second.x,second.y, mx,my)elseif shape.mode == 'circle' thenlocal center = drawing.points[shape.center]if mx < 0 or mx >= 256 or my < 0 or my >= drawing.h thenreturnendelseif shape.mode == 'arc' thenlocal center = drawing.points[shape.center]if mx < 0 or mx >= 256 or my < 0 or my >= drawing.h thenreturnendshape.end_angle = geom.angle_with_hint(center.x,center.y, mx,my, shape.end_angle)love.graphics.arc('line', 'open', cx,cy, Drawing.pixels(shape.radius, width), shape.start_angle, shape.end_angle, 360)elseif shape.mode == 'move' then-- nothing pending; changes are immediately committedelseif shape.mode == 'name' then-- nothing pending; changes are immediately committedelseprint(shape.mode)assert(false)endendlocal width = right-leftreturn y >= line_cache.starty and y < line_cache.starty + Drawing.pixels(drawing.h, width) and x >= left and x < rightendelseassert(false)endend-- a couple of operations on drawings need to constantly check the state of the mouseassert(drawing.mode == 'drawing')if App.mouse_down(1) thenif Drawing.in_drawing(drawing, line_cache, pmx,pmy, State.left,State.right) thenif drawing.pending.mode == 'freehand' thenelseif drawing.pending.mode == 'move' thendrawing.pending.target_point.x = mxdrawing.pending.target_point.y = myDrawing.relax_constraints(drawing, drawing.pending.target_point_index)endenddrawing.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'endendendendendendelseif State.lines.current_drawing thenlocal drawing = State.lines.current_drawinglocal line_cache = State.line_cache[State.lines.current_drawing_index]-- the last point added during update is good enoughendif math.abs(mx-p1.x) > math.abs(my-p1.y) thenelseendendendlocal thirdx,thirdy, fourthx,fourthy = Drawing.complete_rectangle(first.x,first.y, second.x,second.y, mx,my)endelse-- too few points; draw nothingendlocal thirdx,thirdy, fourthx,fourthy = Drawing.complete_square(first.x,first.y, second.x,second.y, mx,my)endendelseif drawing.pending.mode == 'circle' thenif mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thenlocal center = drawing.points[drawing.pending.center]table.insert(drawing.shapes, drawing.pending)drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)table.insert(drawing.shapes, drawing.pending)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))elseif drawing.pending.mode == 'square' thenassert(#drawing.pending.vertices <= 2)if #drawing.pending.vertices == 2 thenif 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 mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)table.insert(drawing.shapes, drawing.pending)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))endendelseif drawing.pending.mode == 'name' then-- drop itelseassert(false)print(drawing.pending.mode)endendendendif chord == 'C-p' and not App.mouse_down(1) thenState.current_drawing_mode = 'freehand'elseif App.mouse_down(1) and chord == 'l' thenif drawing.pending.mode == 'freehand' thenelseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' thendrawing.pending.p1 = drawing.pending.vertices[1]elseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' thendrawing.pending.p1 = drawing.pending.centerenddrawing.pending.mode = 'line'elseif chord == 'C-l' and not App.mouse_down(1) thenelseif App.mouse_down(1) and chord == 'm' thenif drawing.pending.mode == 'freehand' thenelseif drawing.pending.mode == 'line' then-- do nothingelseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' thendrawing.pending.p1 = drawing.pending.vertices[1]elseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' thendrawing.pending.p1 = drawing.pending.centerenddrawing.pending.mode = 'manhattan'elseif chord == 'C-m' and not App.mouse_down(1) thenelseif chord == 'C-g' and not App.mouse_down(1) thenelseif App.mouse_down(1) and chord == 'g' thenif drawing.pending.mode == 'freehand' thenelseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' thenif drawing.pending.vertices == nil thendrawing.pending.vertices = {drawing.pending.p1}end-- reuse existing verticeselseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' thendrawing.pending.vertices = {drawing.pending.center}enddrawing.pending.mode = 'polygon'while #drawing.pending.vertices >= 2 dotable.remove(drawing.pending.vertices)endtable.insert(drawing.pending.vertices, j)drawing.pending.mode = 'arc'local center = drawing.points[drawing.pending.center]drawing.pending.start_angle = geom.angle(center.x,center.y, mx,my)if drawing.pending.mode == 'freehand' thenelseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' thendrawing.pending.center = drawing.pending.p1drawing.pending.center = drawing.pending.vertices[1]enddrawing.pending.mode = 'circle'if drawing thenendelseif chord == 'C-n' and not App.mouse_down(1) thenlocal drawing_index,drawing,line_cache,point_index,p = Drawing.select_point_at_mouse(State)if drawing thenp.name = ''endif drawing thenfor _,shape in ipairs(drawing.shapes) doif Drawing.contains_point(shape, i) thenif shape.mode == 'polygon' thenlocal idx = table.find(shape.vertices, i)assert(idx)table.remove(shape.vertices, idx)if #shape.vertices < 3 thenshape.mode = 'deleted'endelseshape.mode = 'deleted'endendenddrawing.points[i].deleted = trueendif drawing thenshape.mode = 'deleted'endif drawing thendrawing.show_help = trueendendendif drawing.mode == 'drawing' thenendendendreturn nilendif drawing.mode == 'drawing' thenfor i,shape in ipairs(drawing.shapes) doassert(shape)if geom.on_shape(mx,my, drawing, shape) thenendendendendendendif drawing.mode == 'drawing' thenfor i,point in ipairs(drawing.points) doassert(point)endendendendendendif drawing.mode == 'drawing' thenreturn drawingendendendendfunction Drawing.contains_point(shape, p)if shape.mode == 'freehand' then-- not supportedelseif shape.mode == 'line' or shape.mode == 'manhattan' thenreturn shape.p1 == p or shape.p2 == preturn table.find(shape.vertices, p)elseif shape.mode == 'circle' thenreturn shape.center == pelseif shape.mode == 'arc' thenreturn shape.center == p-- ugh, how to support angleselseif shape.mode == 'deleted' then-- already doneelseprint(shape.mode)assert(false)endendfunction Drawing.insert_point(points, x,y)for i,point in ipairs(points) doreturn iendendtable.insert(points, {x=x, y=y})return #pointsendendendendfunction table.find(h, x)for k,v in pairs(h) doif v == x thenreturn kendendfunction Drawing.coord(n, width) -- pixels to partsreturn math.floor(n*256/width)function Drawing.pixels(n, width) -- parts to pixelsreturn math.floor(n*width/256)return (cx-px)*(cx-px) + (cy-py)*(cy-py) < Same_point_distance*Same_point_distancefunction 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)if Drawing.near(point, x,y, width) thentable.insert(points, {x=x, y=y})return #pointsend-- check if UI would snap the two points togetherfunction Drawing.find_or_insert_point(points, x,y, width)function round(num)return math.floor(num+.5)endendendfunction Drawing.smoothen(shape)assert(shape.mode == 'freehand')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]endb.x = round((a.x + b.x + c.x)/3)b.y = round((a.y + b.y + c.y)/3)elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' 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) thenfunction Drawing.select_drawing_at_mouse(State)for drawing_index,drawing in ipairs(State.lines) doif Drawing.near(point, mx,my, State.width) thenreturn drawing_index,drawing,line_cache,i,pointlocal 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)function Drawing.select_point_at_mouse(State)for drawing_index,drawing in ipairs(State.lines) doreturn drawing,line_cache,i,shapelocal 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)function Drawing.select_shape_at_mouse(State)for drawing_index,drawing in ipairs(State.lines) dolocal 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_cachelocal x, y = App.mouse_x(), App.mouse_y()for drawing_index,drawing in ipairs(State.lines) dofunction Drawing.current_drawing(State)local fourthx = firstx+deltaylocal fourthy = firsty-deltaxreturn thirdx,thirdy, fourthx,fourthyendendfunction Drawing.complete_rectangle(firstx,firsty, secondx,secondy, x,y)if firstx == secondx thenendif firsty == secondy thenendlocal 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_slope-- 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_slopereturn 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-deltaxlocal fourthx = round(((firstx-a*firsty) - a*c) / (a*a + 1))local fourthy = round((a*(-firstx + a*firsty) - c) / (a*a + 1))local thirdx = round(((x-a*y) - a*c) / (a*a + 1))local thirdy = round((a*(-x + a*y) - c) / (a*a + 1))return secondx,y, firstx,yreturn x,secondy, x,firstyelseif chord == 'escape' and App.mouse_down(1) thendrawing.pending = {}local _,drawing = Drawing.current_drawing(State)elseif chord == 'C-h' and not App.mouse_down(1) thenlocal drawing = Drawing.select_drawing_at_mouse(State)local drawing,_,_,shape = Drawing.select_shape_at_mouse(State)elseif chord == 'C-d' and not App.mouse_down(1) thenlocal _,drawing,_,i,p = Drawing.select_point_at_mouse(State)drawing.pending = {mode=State.current_drawing_mode, target_point=point_index}State.lines.current_drawing_index = drawing_indexState.lines.current_drawing = drawing-- don't clobberendState.current_drawing_mode = 'name'State.previous_drawing_mode = State.current_drawing_modeif State.previous_drawing_mode == nil thenendState.current_drawing_mode = 'move'drawing.pending = {mode=State.current_drawing_mode, target_point=p, target_point_index=i}State.lines.current_drawing_index = drawing_indexState.lines.current_drawing = drawingif State.previous_drawing_mode == nil thenState.previous_drawing_mode = State.current_drawing_modeelseif chord == 'C-u' and not App.mouse_down(1) thenlocal drawing_index,drawing,line_cache,i,p = Drawing.select_point_at_mouse(State)elseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' thendrawing.pending.center = Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)elseif App.mouse_down(1) and chord == 'o' thenState.current_drawing_mode = 'circle'local _,drawing = Drawing.current_drawing(State)drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)elseif chord == 'C-o' and not App.mouse_down(1) thenState.current_drawing_mode = 'circle'elseif App.mouse_down(1) and chord == 'a' and State.current_drawing_mode == 'circle' thenlocal _,drawing,line_cache = Drawing.current_drawing(State)elseif App.mouse_down(1) and chord == 'r' thenif drawing.pending.mode == 'freehand' thenelseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' thenif drawing.pending.vertices == nil thendrawing.pending.vertices = {drawing.pending.p1}endelseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' thendrawing.pending.vertices = {drawing.pending.center}enddrawing.pending.mode = 'rectangle'elseif App.mouse_down(1) and chord == 's' thenif drawing.pending.mode == 'freehand' thenelseif drawing.pending.mode == 'line' or drawing.pending.mode == 'manhattan' thenif drawing.pending.vertices == nil thendrawing.pending.vertices = {drawing.pending.p1}endelseif drawing.pending.mode == 'polygon' thenwhile #drawing.pending.vertices > 2 dotable.remove(drawing.pending.vertices)endelseif drawing.pending.mode == 'rectangle' then-- reuse existing (1-2) verticeselseif drawing.pending.mode == 'circle' or drawing.pending.mode == 'arc' thendrawing.pending.vertices = {drawing.pending.center}enddrawing.pending.mode = 'square'table.insert(drawing.pending.vertices, j)elseif App.mouse_down(1) and chord == 'p' and (State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square') thenlocal j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)local _,drawing,line_cache = Drawing.current_drawing(State)local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)elseif App.mouse_down(1) and chord == 'p' and State.current_drawing_mode == 'polygon' thenlocal j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)local _,drawing,line_cache = Drawing.current_drawing(State)local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)drawing.pending.vertices = {Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)}State.current_drawing_mode = 'square'local _,drawing = Drawing.current_drawing(State)elseif chord == 'C-s' and not App.mouse_down(1) thenState.current_drawing_mode = 'square'elseif drawing.pending.mode == 'polygon' or drawing.pending.mode == 'square' then-- reuse existing (1-2) verticesdrawing.pending.vertices = {Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)}State.current_drawing_mode = 'rectangle'local _,drawing = Drawing.current_drawing(State)elseif chord == 'C-r' and not App.mouse_down(1) thenState.current_drawing_mode = 'rectangle'elseif drawing.pending.mode == 'rectangle' or drawing.pending.mode == 'square' thendrawing.pending.vertices = {Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)}State.current_drawing_mode = 'polygon'local _,drawing = Drawing.current_drawing(State)State.current_drawing_mode = 'polygon'State.current_drawing_mode = 'manhattan'drawing.pending.p1 = Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)State.current_drawing_mode = 'manhattan'local drawing = Drawing.select_drawing_at_mouse(State)State.current_drawing_mode = 'line'drawing.pending.p1 = Drawing.find_or_insert_point(drawing.points, drawing.pending.points[1].x, drawing.pending.points[1].y, State.width)State.current_drawing_mode = 'line'local _,drawing = Drawing.current_drawing(State)function Drawing.keychord_pressed(State, chord)State.lines.current_drawing.pending = {}State.lines.current_drawing = nilelseif drawing.pending.mode == 'arc' thenif 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)local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)elseif drawing.pending.mode == 'rectangle' thenassert(#drawing.pending.vertices <= 2)if #drawing.pending.vertices == 2 thenif 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 mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)elseif drawing.pending.mode == 'polygon' thenif mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thentable.insert(drawing.shapes, drawing.pending)table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, mx,my, State.width))local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)local p2 = drawing.points[drawing.pending.p2]table.insert(drawing.shapes, drawing.pending)App.mouse_move(State.left+Drawing.pixels(p2.x, State.width), line_cache.starty+Drawing.pixels(p2.y, State.width))drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, p1.x, my, State.width)drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx, p1.y, State.width)elseif drawing.pending.mode == 'manhattan' thenlocal p1 = drawing.points[drawing.pending.p1]if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thenlocal mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)Drawing.smoothen(drawing.pending)table.insert(drawing.shapes, drawing.pending)elseif drawing.pending.mode == 'line' thenif mx >= 0 and mx < 256 and my >= 0 and my < drawing.h thentable.insert(drawing.shapes, drawing.pending)drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)if drawing.pending thenif drawing.pending.mode == nil then-- nothing pendingelseif drawing.pending.mode == 'freehand' thenfunction Drawing.mouse_released(State, x,y, button)if State.current_drawing_mode == 'move' thenState.current_drawing_mode = State.previous_drawing_modeState.previous_drawing_mode = nilif State.lines.current_drawing thenState.lines.current_drawing.pending = {}State.lines.current_drawing = nilelseif State.current_drawing_mode == 'move' thenif Drawing.in_drawing(drawing, line_cache, pmx, pmy, State.left,State.right) thentable.insert(drawing.pending.points, {x=mx, y=my})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)function 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]print(State.current_drawing_mode)local cx = Drawing.coord(x-State.left, State.width)if State.current_drawing_mode == 'freehand' thenelseif State.current_drawing_mode == 'line' or State.current_drawing_mode == 'manhattan' thendrawing.pending = {mode=State.current_drawing_mode, p1=j}elseif State.current_drawing_mode == 'polygon' or State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square' thendrawing.pending = {mode=State.current_drawing_mode, vertices={j}}elseif State.current_drawing_mode == 'circle' thendrawing.pending = {mode=State.current_drawing_mode, center=j}elseif State.current_drawing_mode == 'move' thenlocal j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)local j = Drawing.find_or_insert_point(drawing.points, cx, cy, State.width)drawing.pending = {mode=State.current_drawing_mode, points={{x=cx, y=cy}}}local cy = Drawing.coord(y-line_cache.starty, State.width)-- all the action is in mouse_released-- nothingelseif State.current_drawing_mode == 'name' thenfunction Drawing.mouse_pressed(State, drawing_index, x,y, button)local drawing = State.lines[drawing_index]local line_cache = State.line_cache[drawing_index]function Drawing.in_drawing(drawing, line_cache, x,y, left,right)if line_cache.starty == nil then return false end -- outside current pagelocal cx,cy = px(center.x), py(center.y)love.graphics.circle('line', cx,cy, geom.dist(cx,cy, App.mouse_x(),App.mouse_y()))local cx,cy = px(center.x), py(center.y)love.graphics.line(px(first.x),py(first.y), px(second.x),py(second.y))love.graphics.line(px(second.x),py(second.y), px(thirdx),py(thirdy))love.graphics.line(px(thirdx),py(thirdy), px(fourthx),py(fourthy))love.graphics.line(px(fourthx),py(fourthy), px(first.x),py(first.y))love.graphics.line(px(first.x),py(first.y), pmx,pmy)love.graphics.line(px(first.x),py(first.y), px(second.x),py(second.y))love.graphics.line(px(second.x),py(second.y), px(thirdx),py(thirdy))love.graphics.line(px(thirdx),py(thirdy), px(fourthx),py(fourthy))love.graphics.line(px(fourthx),py(fourthy), px(first.x),py(first.y))love.graphics.line(px(first.x),py(first.y), pmx,pmy)love.graphics.line(px(prev.x),py(prev.y), pmx,pmy)love.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))love.graphics.line(px(p1.x),py(p1.y), px(p1.x),pmy)love.graphics.line(px(p1.x),py(p1.y), pmx, py(p1.y))local p1 = drawing.points[shape.p1]love.graphics.line(px(p1.x),py(p1.y), pmx,pmy)if shape.mode == nil then-- nothing pendingelseif shape.mode == 'freehand' thenlocal shape_copy = deepcopy(shape)Drawing.smoothen(shape_copy)Drawing.draw_shape(drawing, shape_copy, top, left,right)function Drawing.draw_pending_shape(drawing, top, left,right)local width = right-leftlocal pmx,pmy = App.mouse_x(), App.mouse_y()local 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-- after mouse_releasedpmx,pmy = px(mx), py(my)local function px(x) return Drawing.pixels(x, width)+left endlocal function py(y) return Drawing.pixels(y, width)+top endlove.graphics.arc('line', 'open', px(center.x),py(center.y), Drawing.pixels(shape.radius, width), shape.start_angle, shape.end_angle, 360)love.graphics.circle('line', px(center.x),py(center.y), Drawing.pixels(shape.radius, width))-- TODO: cliplove.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))love.graphics.line(px(prev.x),py(prev.y), px(curr.x),py(curr.y))love.graphics.line(px(p1.x),py(p1.y), px(p2.x),py(p2.y))love.graphics.line(px(prev.x),py(prev.y), px(point.x),py(point.y))function Drawing.draw_shape(drawing, shape, top, left,right)local width = right-leftlocal function px(x) return Drawing.pixels(x, width)+left endlocal function py(y) return Drawing.pixels(y, width)+top endApp.color(Current_stroke_color)Drawing.draw_pending_shape(line, line_cache.starty, State.left,State.right)App.color(Stroke_color)love.graphics.circle('fill', px(p.x),py(p.y), 2)App.color(Focus_stroke_color)love.graphics.circle('line', px(p.x),py(p.y), Same_point_distance)if Drawing.near(p, mx,my, State.width) thenlocal function px(x) return Drawing.pixels(x, State.width)+State.left endlocal function py(y) return Drawing.pixels(y, State.width)+line_cache.starty endfor i,p in ipairs(line.points) doDrawing.draw_shape(line, shape, line_cache.starty, State.left,State.right)App.color(Stroke_color)if geom.on_shape(mx,my, line, shape) thenApp.color(Focus_stroke_color)local mx = Drawing.coord(pmx-State.left, State.width)local my = Drawing.coord(pmy-line_cache.starty, State.width)draw_help_without_mouse_pressed(State, line_index)if App.mouse_down(1) and love.keyboard.isDown('h') thendraw_help_with_mouse_pressed(State, line_index)icon[State.previous_drawing_mode](State.right-22, line_cache.starty+4)-- All drawings span 100% of some conceptual 'page width' and divide it up-- into 256 parts.local pmx,pmy = App.mouse_x(), App.mouse_y()App.color(Icon_color)if icon[State.current_drawing_mode] thenicon[State.current_drawing_mode](State.right-22, line_cache.starty+4)love.graphics.rectangle('line', State.left,line_cache.starty, State.width,Drawing.pixels(line.h, State.width))if pmx < State.right and pmy > line_cache.starty and pmy < line_cache.starty+Drawing.pixels(line.h, State.width) thenfunction Drawing.draw(State, line_index, y)local line = State.lines[line_index]local line_cache = State.line_cache[line_index]line_cache.starty = y - file deletion: icons.lua
icon = {}function icon.insert_drawing(x, y)love.graphics.rectangle('line', x,y, 12,12)love.graphics.line(4,y+6, 16,y+6)love.graphics.line(10,y, 10,y+12)endfunction icon.freehand(x, y)love.graphics.line(x+4,y+7,x+5,y+5)love.graphics.line(x+5,y+5,x+7,y+4)love.graphics.line(x+7,y+4,x+9,y+3)love.graphics.line(x+9,y+3,x+10,y+5)love.graphics.line(x+10,y+5,x+12,y+6)love.graphics.line(x+12,y+6,x+13,y+8)love.graphics.line(x+13,y+8,x+13,y+10)love.graphics.line(x+13,y+10,x+14,y+12)love.graphics.line(x+14,y+12,x+15,y+14)love.graphics.line(x+15,y+14,x+15,y+16)endfunction icon.line(x, y)love.graphics.line(x+4,y+2, x+16,y+18)endfunction icon.manhattan(x, y)love.graphics.line(x+4,y+20, x+4,y+2)love.graphics.line(x+4,y+2, x+10,y+2)love.graphics.line(x+10,y+2, x+10,y+10)love.graphics.line(x+10,y+10, x+18,y+10)endfunction icon.polygon(x, y)love.graphics.line(x+8,y+2, x+14,y+2)love.graphics.line(x+14,y+2, x+18,y+10)love.graphics.line(x+18,y+10, x+10,y+18)love.graphics.line(x+10,y+18, x+4,y+12)love.graphics.line(x+4,y+12, x+8,y+2)endfunction icon.circle(x, y)love.graphics.circle('line', x+10,y+10, 8)endfunction icon.square(x, y)love.graphics.line(x+6,y+6, x+6,y+16)love.graphics.line(x+6,y+16, x+16,y+16)love.graphics.line(x+16,y+16, x+16,y+6)love.graphics.line(x+16,y+6, x+6,y+6)endendfunction icon.rectangle(x, y)love.graphics.line(x+4,y+8, x+4,y+16)love.graphics.line(x+4,y+16, x+16,y+16)love.graphics.line(x+16,y+16, x+16,y+8)love.graphics.line(x+16,y+8, x+4,y+8)App.color(Icon_color) - file deletion: help.lua
love.graphics.print("Things you can do:", State.left+30,y)y = y + State.line_heighty = y + State.line_heighty = y + State.line_heighty = y + State.line_heighty = y + State.line_heighty = y + State.line_heightif State.current_drawing_mode ~= 'freehand' theny = y + State.line_heightendendendendendendendendlove.graphics.print("You're currently drawing a "..current_shape(State, drawing.pending), State.left+30,y)y = y + State.line_heighty = y + State.line_heightif State.current_drawing_mode == 'freehand' theny = y + State.line_heightelseif State.current_drawing_mode == 'line' or State.current_drawing_mode == 'manhattan' theny = y + State.line_heightelseif State.current_drawing_mode == 'circle' thenif drawing.pending.mode == 'circle' thenelseendendendendendendendendendreturn 'freehand stroke'return 'straight line'return 'horizontal/vertical line'return 'arc'elseendend_bullet_indent = nilfunction bullet_indent()if _bullet_indent == nil thenlocal text = love.graphics.newText(love.graphics.getFont(), '* ')_bullet_indent = text:getWidth()endreturn _bullet_indentendreturn State.current_drawing_modeelseif State.current_drawing_mode == 'circle' and shape and shape.start_angle thenelseif State.current_drawing_mode == 'manhattan' thenelseif State.current_drawing_mode == 'line' thenfunction current_shape(State, shape)if State.current_drawing_mode == 'freehand' thenApp.color(Help_background_color)love.graphics.rectangle('fill', State.left,line_cache.starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-line_cache.starty))if State.current_drawing_mode ~= 'square' theny = y + State.line_heightlove.graphics.print("* Press 's' to switch to drawing squares", State.left+30,y)if State.current_drawing_mode ~= 'rectangle' theny = y + State.line_heightlove.graphics.print("* Press 'r' to switch to drawing rectangles", State.left+30,y)if State.current_drawing_mode ~= 'polygon' theny = y + State.line_heightlove.graphics.print("* Press 'g' to switch to drawing polygons", State.left+30,y)if State.current_drawing_mode ~= 'circle' theny = y + State.line_heightlove.graphics.print("* Press 'o' to switch to drawing circles/arcs", State.left+30,y)if State.current_drawing_mode ~= 'manhattan' theny = y + State.line_heightlove.graphics.print("* Press 'm' to switch to drawing horizontal/vertical lines", State.left+30,y)y = y + State.line_heighty = y + State.line_heightif State.current_drawing_mode ~= 'line' theny = y + State.line_heightlove.graphics.print("* Press 'l' to switch to drawing lines", State.left+30,y)love.graphics.print("* Press 'esc' then release the mouse button to cancel the current shape", State.left+30,y)if #drawing.pending.vertices < 2 theny = y + State.line_heightelsey = y + State.line_heighty = y + State.line_heightlove.graphics.print("* Press 'p' to replace the second vertex of the rectangle", State.left+30,y)endif #drawing.pending.vertices < 2 theny = y + State.line_heightelsey = y + State.line_heighty = y + State.line_heightlove.graphics.print("* Press 'p' to replace the second vertex of the square", State.left+30,y)endlove.graphics.print('* Release the mouse button to finish drawing the square', State.left+30,y)love.graphics.print("* Press 'p' to add a vertex to the square", State.left+30,y)elseif State.current_drawing_mode == 'square' thenlove.graphics.print('* Release the mouse button to finish drawing the rectangle', State.left+30,y)love.graphics.print("* Press 'p' to add a vertex to the rectangle", State.left+30,y)y = y + State.line_heightelseif State.current_drawing_mode == 'polygon' theny = y + State.line_heighty = y + State.line_heightelseif State.current_drawing_mode == 'rectangle' thenlove.graphics.print("* Press 'p' to add a vertex to the polygon", State.left+30,y)love.graphics.print('* Release the mouse button to finish drawing the polygon', State.left+30,y)love.graphics.print('* Release the mouse button to finish drawing the arc', State.left+30,y)y = y + State.line_heightlove.graphics.print("* Press 'a' to draw just an arc of a circle", State.left+30,y)love.graphics.print('* Release the mouse button to finish drawing the circle', State.left+30,y)love.graphics.print('* Release the mouse button to finish drawing the line', State.left+30,y)love.graphics.print('* Release the mouse button to finish drawing the stroke', State.left+30,y)love.graphics.print('Things you can do now:', State.left+30,y)App.color(Help_color)local y = line_cache.starty+10function draw_help_with_mouse_pressed(State, drawing_index)local drawing = State.lines[drawing_index]local line_cache = State.line_cache[drawing_index]y = y + State.line_heighty = y + State.line_heightlove.graphics.print("Press 'esc' now to hide this message", State.left+30,y)App.color(Help_background_color)love.graphics.rectangle('fill', State.left,line_cache.starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-line_cache.starty))love.graphics.print("* Press 'ctrl+=' or 'ctrl+-' to zoom in or out, ctrl+0 to reset zoom", State.left+30,y)if State.current_drawing_mode ~= 'square' theny = y + State.line_heightlove.graphics.print("* Press 'ctrl+s' to switch to drawing squares", State.left+30,y)if State.current_drawing_mode ~= 'rectangle' theny = y + State.line_heightlove.graphics.print("* Press 'ctrl+r' to switch to drawing rectangles", State.left+30,y)if State.current_drawing_mode ~= 'polygon' theny = y + State.line_heightlove.graphics.print("* Press 'ctrl+g' to switch to drawing polygons", State.left+30,y)if State.current_drawing_mode ~= 'circle' theny = y + State.line_heightlove.graphics.print("* Press 'ctrl+o' to switch to drawing circles/arcs", State.left+30,y)if State.current_drawing_mode ~= 'manhattan' theny = y + State.line_heightlove.graphics.print("* Press 'ctrl+m' to switch to drawing horizontal/vertical lines", State.left+30,y)if State.current_drawing_mode ~= 'line' theny = y + State.line_heightlove.graphics.print("* Press 'ctrl+l' to switch to drawing lines", State.left+30,y)love.graphics.print("* Press 'ctrl+p' to switch to drawing freehand strokes", State.left+30,y)love.graphics.print("* Hover on a point or shape and press 'ctrl+d' to delete it", State.left+30,y)love.graphics.print("* Hover on a point and press 'ctrl+n', type a name, then press 'enter'", State.left+30,y)love.graphics.print("then press the mouse button to drop it", State.left+30+bullet_indent(),y)love.graphics.print("* Hover on a point and press 'ctrl+u' to pick it up and start moving it,", State.left+30,y)love.graphics.print("* Press the mouse button to start drawing a "..current_shape(State), State.left+30,y)App.color(Help_color)local y = line_cache.starty+10function draw_help_without_mouse_pressed(State, drawing_index)local drawing = State.lines[drawing_index]local line_cache = State.line_cache[drawing_index] - file deletion: geom.lua
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' thenreturn geom.on_polygon(x,y, drawing, shape)elseif shape.mode == 'circle' thenlocal center = drawing.points[shape.center]elseif shape.mode == 'arc' thenlocal center = drawing.points[shape.center]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 thenreturn falseendlocal y1,y2 = p1.y,p2.yif y1 > y2 theny1,y2 = y2,y1endend-- has the right slope and interceptlocal m = (p2.y - p1.y) / (p2.x - p1.x)local yp = p1.y + m*(x-p1.x)return falseend-- between endpointslocal k = (x-p1.x) / (p2.x-p1.x)endfunction 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]})endfunction 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)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 endlocal angle = geom.angle(ox,oy, x,y)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 -1endreturn k > -0.005 and k < 1.005if yp < y-2 or yp > y+2 thenreturn y >= y1-2 and y <= y2+2if math.abs(p1.x-x) > 2 thenlocal dist = geom.dist(center.x,center.y, x,y)local dist = geom.dist(center.x,center.y, x,y)return dist > shape.radius*0.95 and dist < shape.radius*1.05elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' 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,y1endelseif p1.y == p2.y thenif y ~= p1.y then return false endlocal x1,x2 = p1.x, p2.xif x1 > x2 thenx1,x2 = x2,x1endendreturn x >= x1-2 and x <= x2+2return y >= y1-2 and y <= y2+2geom = {} - replacement in undo.lua at line 63[27.295]→[27.1592:1978](∅→∅),[27.1017]→[27.1592:1978](∅→∅),[27.1102]→[27.1592:1978](∅→∅),[27.1592]→[27.1592:1978](∅→∅),[27.1978]→[16.1:239](∅→∅),[16.239]→[27.2236:2296](∅→∅),[27.2236]→[27.2236:2296](∅→∅)
if line.mode == 'text' thentable.insert(event.lines, {mode='text', data=line.data})elseif line.mode == 'drawing' thenlocal points=deepcopy(line.points)--? print('copying', line.points, 'with', #line.points, 'points into', points)local shapes=deepcopy(line.shapes)--? print('copying', line.shapes, 'with', #line.shapes, 'shapes into', shapes)table.insert(event.lines, {mode='drawing', h=line.h, points=points, shapes=shapes, pending={}})--? table.insert(event.lines, {mode='drawing', h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}})elseprint(line.mode)assert(false)endtable.insert(event.lines, State.lines[i]) - edit in text_tests.lua at line 15[27.1623]→[27.474:604](∅→∅),[27.474]→[27.474:604](∅→∅),[27.604]→[27.49:95](∅→∅),[27.95]→[27.1624:1660](∅→∅),[27.211]→[27.1624:1660](∅→∅),[27.604]→[27.1624:1660](∅→∅),[27.1660]→[27.348:380](∅→∅),[27.380]→[27.29:55](∅→∅),[27.1660]→[27.29:55](∅→∅),[27.55]→[27.212:280](∅→∅),[27.77]→[27.687:738](∅→∅),[27.280]→[27.687:738](∅→∅),[27.1807]→[27.687:738](∅→∅),[27.687]→[27.687:738](∅→∅),[27.738]→[27.1808:1970](∅→∅),[27.1970]→[27.874:1115](∅→∅),[27.874]→[27.874:1115](∅→∅),[27.1115]→[27.96:142](∅→∅),[27.142]→[27.1971:2028](∅→∅),[27.385]→[27.1971:2028](∅→∅),[27.1115]→[27.1971:2028](∅→∅),[27.2028]→[27.381:413](∅→∅),[27.145]→[27.1159:1244](∅→∅),[27.413]→[27.1159:1244](∅→∅),[27.2114]→[27.1159:1244](∅→∅),[27.1159]→[27.1159:1244](∅→∅),[27.1244]→[27.2115:2147](∅→∅),[27.2147]→[27.1263:1300](∅→∅),[27.1263]→[27.1263:1300](∅→∅),[27.1300]→[27.78:131](∅→∅),[27.131]→[27.2148:2318](∅→∅),[27.1338]→[27.2148:2318](∅→∅)
endfunction test_click_to_create_drawing()io.write('\ntest_click_to_create_drawing')App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{}Text.redraw_all(Editor_state)edit.draw(Editor_state)edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1)-- cursor skips drawing to always remain on textcheck_eq(#Editor_state.lines, 2, 'F - test_click_to_create_drawing/#lines')check_eq(Editor_state.cursor1.line, 2, 'F - test_click_to_create_drawing/cursor')endfunction test_backspace_to_delete_drawing()io.write('\ntest_backspace_to_delete_drawing')-- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end)App.screen.init{width=120, height=60}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'```lines', '```', ''}Text.redraw_all(Editor_state)-- cursor is on text as always (outside tests this will get initialized correctly)Editor_state.cursor1.line = 2-- backspacing deletes the drawingedit.run_after_keychord(Editor_state, 'backspace')check_eq(#Editor_state.lines, 1, 'F - test_backspace_to_delete_drawing/#lines')check_eq(Editor_state.cursor1.line, 1, 'F - test_backspace_to_delete_drawing/cursor') - replacement in text_tests.lua at line 604
check_eq(Editor_state.lines[1].data, 'abc', 'F - test_cursor_movement_without_shift_resets_selection/data')check_eq(Editor_state.lines[1], 'abc', 'F - test_cursor_movement_without_shift_resets_selection/data') - replacement in text_tests.lua at line 622
check_eq(Editor_state.lines[1].data, 'xbc', 'F - test_edit_deletes_selection')check_eq(Editor_state.lines[1], 'xbc', 'F - test_edit_deletes_selection') - replacement in text_tests.lua at line 645
check_eq(Editor_state.lines[1].data, 'Dbc', 'F - test_edit_with_shift_key_deletes_selection/data')check_eq(Editor_state.lines[1], 'Dbc', 'F - test_edit_with_shift_key_deletes_selection/data') - replacement in text_tests.lua at line 683
check_eq(Editor_state.lines[1].data, 'bc', 'F - test_cut/data')check_eq(Editor_state.lines[1], 'bc', 'F - test_cut/data') - replacement in text_tests.lua at line 704
check_eq(Editor_state.lines[1].data, 'xyzdef', 'F - test_paste_replaces_selection')check_eq(Editor_state.lines[1], 'xyzdef', 'F - test_paste_replaces_selection') - replacement in text_tests.lua at line 730
check_eq(Editor_state.lines[1].data, 'ahi', 'F - test_deleting_selection_may_scroll/data')check_eq(Editor_state.lines[1], 'ahi', 'F - test_deleting_selection_may_scroll/data') - replacement in text_tests.lua at line 796
check_eq(Editor_state.lines[1].data, '', 'F - test_insert_newline_at_start_of_line/data:1')check_eq(Editor_state.lines[2].data, 'abc', 'F - test_insert_newline_at_start_of_line/data:2')check_eq(Editor_state.lines[1], '', 'F - test_insert_newline_at_start_of_line/data:1')check_eq(Editor_state.lines[2], 'abc', 'F - test_insert_newline_at_start_of_line/data:2') - edit in text_tests.lua at line 967[27.7115]→[27.7115:7255](∅→∅),[27.7255]→[27.2:29](∅→∅),[27.29]→[27.7521:7589](∅→∅),[27.7589]→[27.2352:2398](∅→∅),[27.2398]→[27.24425:24493](∅→∅),[27.7693]→[27.24425:24493](∅→∅),[27.24425]→[27.24425:24493](∅→∅),[27.24493]→[27.2315:2551](∅→∅),[27.2551]→[27.24580:24802](∅→∅),[27.24580]→[27.24580:24802](∅→∅),[27.24802]→[27.2:80](∅→∅),[27.80]→[27.7779:7946](∅→∅),[27.85]→[27.7779:7946](∅→∅),[27.170]→[27.7779:7946](∅→∅),[27.24893]→[27.7779:7946](∅→∅),[27.7779]→[27.7779:7946](∅→∅),[27.7946]→[27.1536:1562](∅→∅),[27.1562]→[27.7694:7723](∅→∅),[27.7723]→[27.7982:8222](∅→∅),[27.24930]→[27.7982:8222](∅→∅),[27.7982]→[27.7982:8222](∅→∅),[27.8222]→[27.3759:3811](∅→∅),[27.3811]→[27.24931:25107](∅→∅),[27.8259]→[27.24931:25107](∅→∅),[27.25107]→[27.7724:7764](∅→∅),[27.7764]→[27.8443:8517](∅→∅),[27.25154]→[27.8443:8517](∅→∅),[27.8443]→[27.8443:8517](∅→∅)
endfunction test_pagedown_skips_drawings()io.write('\ntest_pagedown_skips_drawings')-- some lines of text with a drawing intermixedlocal drawing_width = 50App.screen.init{width=Editor_state.left+drawing_width, height=80}Editor_state = edit.initialize_test_state()Editor_state.lines = load_array{'abc', -- height 15'```lines', '```', -- height 25'def', -- height 15'ghi'} -- height 15Text.redraw_all(Editor_state)check_eq(Editor_state.lines[2].mode, 'drawing', 'F - test_pagedown_skips_drawings/baseline/lines')Editor_state.cursor1 = {line=1, pos=1}Editor_state.screen_top1 = {line=1, pos=1}Editor_state.screen_bottom1 = {}local drawing_height = Drawing_padding_height + drawing_width/2 -- default-- initially the screen displays the first line and the drawing-- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80pxedit.draw(Editor_state)local y = Editor_state.topApp.screen.check(y, 'abc', 'F - test_pagedown_skips_drawings/baseline/screen:1')-- after pagedown the screen draws the drawing up top-- 15px margin + 10px margin + 25px drawing + 10px margin + 15px line3 = 75px < screen height 80pxedit.run_after_keychord(Editor_state, 'pagedown')check_eq(Editor_state.screen_top1.line, 2, 'F - test_pagedown_skips_drawings/screen_top')check_eq(Editor_state.cursor1.line, 3, 'F - test_pagedown_skips_drawings/cursor')y = Editor_state.top + drawing_heightApp.screen.check(y, 'def', 'F - test_pagedown_skips_drawings/screen:1') - replacement in text_tests.lua at line 1761
check_eq(Editor_state.lines[1].data, 'abcdef', "F - test_backspace_past_line_boundary")check_eq(Editor_state.lines[1], 'abcdef', "F - test_backspace_past_line_boundary") - replacement in text_tests.lua at line 1778
check_eq(Editor_state.lines[1].data, 'bc', "F - test_backspace_over_selection/data")check_eq(Editor_state.lines[1], 'bc', "F - test_backspace_over_selection/data") - replacement in text_tests.lua at line 1797
check_eq(Editor_state.lines[1].data, 'bc', "F - test_backspace_over_selection_reverse/data")check_eq(Editor_state.lines[1], 'bc', "F - test_backspace_over_selection_reverse/data") - replacement in text_tests.lua at line 1816
check_eq(Editor_state.lines[1].data, 'akl', "F - test_backspace_over_multiple_lines/data:1")check_eq(Editor_state.lines[2].data, 'mno', "F - test_backspace_over_multiple_lines/data:2")check_eq(Editor_state.lines[1], 'akl', "F - test_backspace_over_multiple_lines/data:1")check_eq(Editor_state.lines[2], 'mno', "F - test_backspace_over_multiple_lines/data:2") - replacement in text_tests.lua at line 1836
check_eq(Editor_state.lines[1].data, 'a', "F - test_backspace_to_start_of_line/data:1")check_eq(Editor_state.lines[2].data, 'def', "F - test_backspace_to_start_of_line/data:2")check_eq(Editor_state.lines[1], 'a', "F - test_backspace_to_start_of_line/data:1")check_eq(Editor_state.lines[2], 'def', "F - test_backspace_to_start_of_line/data:2") - replacement in text_tests.lua at line 1856
check_eq(Editor_state.lines[1].data, 'abc', "F - test_backspace_to_start_of_line/data:1")check_eq(Editor_state.lines[2].data, 'f', "F - test_backspace_to_start_of_line/data:2")check_eq(Editor_state.lines[1], 'abc', "F - test_backspace_to_start_of_line/data:1")check_eq(Editor_state.lines[2], 'f', "F - test_backspace_to_start_of_line/data:2") - replacement in text_tests.lua at line 1953
check_eq(Editor_state.lines[1].data, 'xbc', 'F - test_undo_restores_selection/baseline')check_eq(Editor_state.lines[1], 'xbc', 'F - test_undo_restores_selection/baseline') - replacement in text_tests.lua at line 1967
Editor_state.lines = load_array{'```lines', '```', 'def', 'ghi', 'deg'}Editor_state.lines = load_array{'abc', 'def', 'ghi', 'deg'} - replacement in text.lua at line 53
if State.lines[State.cursor1.line].data:sub(State.cursor1.pos, State.cursor1.pos+utf8.len(State.search_term)-1) == State.search_term thenif State.lines[State.cursor1.line]:sub(State.cursor1.pos, State.cursor1.pos+utf8.len(State.search_term)-1) == State.search_term then - replacement in text.lua at line 95
for frag in line.data:gmatch('%S*%s*') dofor frag in line:gmatch('%S*%s*') do - replacement in text.lua at line 145
local 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)local byte_offset = Text.offset(State.lines[State.cursor1.line], State.cursor1.pos)State.lines[State.cursor1.line] = string.sub(State.lines[State.cursor1.line], 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line], byte_offset) - replacement in text.lua at line 185
local byte_start = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos-1)local byte_end = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)local byte_start = utf8.offset(State.lines[State.cursor1.line], State.cursor1.pos-1)local byte_end = utf8.offset(State.lines[State.cursor1.line], State.cursor1.pos) - replacement in text.lua at line 189
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)..string.sub(State.lines[State.cursor1.line].data, byte_end)State.lines[State.cursor1.line] = string.sub(State.lines[State.cursor1.line], 1, byte_start-1)..string.sub(State.lines[State.cursor1.line], byte_end) - replacement in text.lua at line 191
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)State.lines[State.cursor1.line] = string.sub(State.lines[State.cursor1.line], 1, byte_start-1) - replacement in text.lua at line 197[27.1585]→[27.2023:2145](∅→∅),[27.2023]→[27.2023:2145](∅→∅),[27.2145]→[27.105:166](∅→∅),[27.166]→[27.2728:2761](∅→∅),[27.2145]→[27.2728:2761](∅→∅),[27.2527]→[27.2728:2761](∅→∅),[27.4205]→[27.2728:2761](∅→∅),[27.59583]→[27.2728:2761](∅→∅),[27.2728]→[27.2728:2761](∅→∅),[27.2761]→[27.2146:2405](∅→∅),[27.2405]→[27.167:226](∅→∅),[27.226]→[27.2948:2958](∅→∅),[27.2405]→[27.2948:2958](∅→∅),[27.2767]→[27.2948:2958](∅→∅),[27.4270]→[27.2948:2958](∅→∅),[27.59920]→[27.2948:2958](∅→∅),[27.2948]→[27.2948:2958](∅→∅)
if State.lines[State.cursor1.line-1].mode == 'drawing' thentable.remove(State.lines, State.cursor1.line-1)table.remove(State.line_cache, State.cursor1.line-1)else-- join linesState.cursor1.pos = utf8.len(State.lines[State.cursor1.line-1].data)+1State.lines[State.cursor1.line-1].data = State.lines[State.cursor1.line-1].data..State.lines[State.cursor1.line].datatable.remove(State.lines, State.cursor1.line)table.remove(State.line_cache, State.cursor1.line)end-- join linesState.cursor1.pos = utf8.len(State.lines[State.cursor1.line-1])+1State.lines[State.cursor1.line-1] = State.lines[State.cursor1.line-1]..State.lines[State.cursor1.line]table.remove(State.lines, State.cursor1.line)table.remove(State.line_cache, State.cursor1.line) - replacement in text.lua at line 225
if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) thenif State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line]) then - replacement in text.lua at line 230
if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) thenlocal byte_start = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)local byte_end = utf8.offset(State.lines[State.cursor1.line].data, State.cursor1.pos+1)if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line]) thenlocal byte_start = utf8.offset(State.lines[State.cursor1.line], State.cursor1.pos)local byte_end = utf8.offset(State.lines[State.cursor1.line], State.cursor1.pos+1) - replacement in text.lua at line 235
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)..string.sub(State.lines[State.cursor1.line].data, byte_end)State.lines[State.cursor1.line] = string.sub(State.lines[State.cursor1.line], 1, byte_start-1)..string.sub(State.lines[State.cursor1.line], byte_end) - replacement in text.lua at line 237
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_start-1)State.lines[State.cursor1.line] = string.sub(State.lines[State.cursor1.line], 1, byte_start-1) - replacement in text.lua at line 242[27.3907]→[27.4271:4334](∅→∅),[27.4334]→[27.3754:3776](∅→∅),[27.3754]→[27.3754:3776](∅→∅),[27.3776]→[27.4030:4154](∅→∅),[27.3646]→[27.3904:3914](∅→∅),[27.4210]→[27.3904:3914](∅→∅),[27.62131]→[27.3904:3914](∅→∅),[27.3904]→[27.3904:3914](∅→∅)
if State.lines[State.cursor1.line+1].mode == 'text' then-- join linesState.lines[State.cursor1.line].data = State.lines[State.cursor1.line].data..State.lines[State.cursor1.line+1].dataend-- join linesState.lines[State.cursor1.line] = State.lines[State.cursor1.line]..State.lines[State.cursor1.line+1] - replacement in text.lua at line 336
local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)table.insert(State.lines, State.cursor1.line+1, {mode='text', data=string.sub(State.lines[State.cursor1.line].data, byte_offset)})local byte_offset = Text.offset(State.lines[State.cursor1.line], State.cursor1.pos)table.insert(State.lines, State.cursor1.line+1, string.sub(State.lines[State.cursor1.line], byte_offset)) - replacement in text.lua at line 339
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)State.lines[State.cursor1.line] = string.sub(State.lines[State.cursor1.line], 1, byte_offset-1) - replacement in text.lua at line 354[27.2828]→[27.2828:2993](∅→∅),[27.2993]→[27.82:188](∅→∅),[27.115]→[27.555:563](∅→∅),[27.168]→[27.555:563](∅→∅),[27.188]→[27.555:563](∅→∅),[27.3092]→[27.555:563](∅→∅),[27.66841]→[27.555:563](∅→∅),[27.555]→[27.555:563](∅→∅)
if State.lines[State.screen_top1.line].mode == 'text' theny = y - State.line_heightelseif State.lines[State.screen_top1.line].mode == 'drawing' theny = y - Drawing_padding_height - Drawing.pixels(State.lines[State.screen_top1.line].h, State.width)endy = y - State.line_height - edit in text.lua at line 391
assert(State.lines[State.cursor1.line].mode == 'text') - replacement in text.lua at line 396[27.443]→[27.4030:4077](∅→∅),[27.4077]→[27.484:559](∅→∅),[27.67955]→[27.484:559](∅→∅),[27.484]→[27.484:559](∅→∅),[27.559]→[27.4078:4136](∅→∅),[27.4136]→[27.611:657](∅→∅),[27.68021]→[27.611:657](∅→∅),[27.611]→[27.611:657](∅→∅),[27.657]→[27.4137:4182](∅→∅),[27.4182]→[27.208:282](∅→∅),[27.251]→[27.826:937](∅→∅),[27.282]→[27.826:937](∅→∅),[27.365]→[27.826:937](∅→∅),[27.1023]→[27.826:937](∅→∅),[27.4275]→[27.826:937](∅→∅),[27.68181]→[27.826:937](∅→∅),[27.779]→[27.826:937](∅→∅),[27.937]→[27.347:450](∅→∅),[27.450]→[27.1023:1264](∅→∅),[27.4374]→[27.1023:1264](∅→∅),[27.4627]→[27.1023:1264](∅→∅),[27.68294]→[27.1023:1264](∅→∅),[27.1023]→[27.1023:1264](∅→∅),[27.1264]→[27.4375:4655](∅→∅),[27.4655]→[27.1508:1520](∅→∅),[27.68617]→[27.1508:1520](∅→∅),[27.1508]→[27.1508:1520](∅→∅),[27.1520]→[27.4656:4882](∅→∅),[27.4882]→[27.1024:1138](∅→∅),[27.99]→[27.1691:1705](∅→∅),[27.1138]→[27.1691:1705](∅→∅),[27.4990]→[27.1691:1705](∅→∅),[27.68994]→[27.1691:1705](∅→∅),[27.1691]→[27.1691:1705](∅→∅)
local new_cursor_line = State.cursor1.linewhile new_cursor_line > 1 donew_cursor_line = new_cursor_line-1if State.lines[new_cursor_line].mode == 'text' then--? print('found previous text line')State.cursor1.line = new_cursor_lineText.populate_screen_line_starting_pos(State, State.cursor1.line)-- previous text line found, pick its final screen line--? print('has multiple screen lines')local screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos--? print(#screen_line_starting_pos)screen_line_starting_pos = screen_line_starting_pos[#screen_line_starting_pos]--? print('previous screen line starts at pos '..tostring(screen_line_starting_pos)..' of its line')if State.screen_top1.line > State.cursor1.line thenState.screen_top1.line = State.cursor1.lineState.screen_top1.pos = screen_line_starting_pos--? print('pos of top of screen is also '..tostring(State.screen_top1.pos)..' of the same line')endlocal screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, screen_line_starting_pos)local s = string.sub(State.lines[State.cursor1.line].data, screen_line_starting_byte_offset)State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1breakif State.cursor1.line > 1 thenlocal new_cursor_line = State.cursor1.line-1--? print('found previous text line')State.cursor1.line = new_cursor_lineText.populate_screen_line_starting_pos(State, State.cursor1.line)-- previous text line found, pick its final screen line--? print('has multiple screen lines')local screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos--? print(#screen_line_starting_pos)screen_line_starting_pos = screen_line_starting_pos[#screen_line_starting_pos]--? print('previous screen line starts at pos '..tostring(screen_line_starting_pos)..' of its line')if State.screen_top1.line > State.cursor1.line thenState.screen_top1.line = State.cursor1.lineState.screen_top1.pos = screen_line_starting_pos--? print('pos of top of screen is also '..tostring(State.screen_top1.pos)..' of the same line') - edit in text.lua at line 412
local screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line], screen_line_starting_pos)local s = string.sub(State.lines[State.cursor1.line], screen_line_starting_byte_offset)State.cursor1.pos = screen_line_starting_pos + Text.nearest_cursor_pos(s, State.cursor_x, State.left) - 1 - replacement in text.lua at line 429
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)local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line], new_screen_line_starting_pos)local s = string.sub(State.lines[State.cursor1.line], new_screen_line_starting_byte_offset) - edit in text.lua at line 437
assert(State.lines[State.cursor1.line].mode == 'text') - replacement in text.lua at line 441[27.3017]→[27.6133:6224](∅→∅),[27.6224]→[27.3096:3138](∅→∅),[27.70385]→[27.3096:3138](∅→∅),[27.3096]→[27.3096:3138](∅→∅),[27.3138]→[27.6225:6328](∅→∅),[27.6328]→[27.1332:1450](∅→∅),[27.1450]→[27.6440:6477](∅→∅),[27.6440]→[27.6440:6477](∅→∅),[27.6477]→[27.3342:3356](∅→∅),[27.70687]→[27.3342:3356](∅→∅),[27.3342]→[27.3342:3356](∅→∅),[27.3356]→[27.2689:2699](∅→∅),[27.2689]→[27.2689:2699](∅→∅)
local new_cursor_line = State.cursor1.linewhile new_cursor_line < #State.lines donew_cursor_line = new_cursor_line+1if State.lines[new_cursor_line].mode == 'text' thenState.cursor1.line = new_cursor_lineState.cursor1.pos = Text.nearest_cursor_pos(State.lines[State.cursor1.line].data, State.cursor_x, State.left)--? print(State.cursor1.pos)breakendif State.cursor1.line < #State.lines thenlocal new_cursor_line = State.cursor1.line+1State.cursor1.line = new_cursor_lineState.cursor1.pos = Text.nearest_cursor_pos(State.lines[State.cursor1.line], State.cursor_x, State.left)--? print(State.cursor1.pos) - replacement in text.lua at line 463
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)local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line], new_screen_line_starting_pos)local s = string.sub(State.lines[State.cursor1.line], new_screen_line_starting_byte_offset) - replacement in text.lua at line 484
State.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data) + 1State.cursor1.pos = utf8.len(State.lines[State.cursor1.line]) + 1 - replacement in text.lua at line 498
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%S') thenif Text.match(State.lines[State.cursor1.line], State.cursor1.pos-1, '%S') then - replacement in text.lua at line 510
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%s') thenif Text.match(State.lines[State.cursor1.line], State.cursor1.pos-1, '%s') then - replacement in text.lua at line 519
if State.cursor1.pos > utf8.len(State.lines[State.cursor1.line].data) thenif State.cursor1.pos > utf8.len(State.lines[State.cursor1.line]) then - replacement in text.lua at line 522
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos, '%S') thenif Text.match(State.lines[State.cursor1.line], State.cursor1.pos, '%S') then - replacement in text.lua at line 529
if State.cursor1.pos > utf8.len(State.lines[State.cursor1.line].data) thenif State.cursor1.pos > utf8.len(State.lines[State.cursor1.line]) then - replacement in text.lua at line 532
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos, '%s') thenif Text.match(State.lines[State.cursor1.line], State.cursor1.pos, '%s') then - edit in text.lua at line 551
assert(State.lines[State.cursor1.line].mode == 'text') - replacement in text.lua at line 553[27.321]→[27.171:178](∅→∅),[27.73224]→[27.171:178](∅→∅),[27.171]→[27.171:178](∅→∅),[27.178]→[27.322:369](∅→∅),[27.369]→[27.219:294](∅→∅),[27.73279]→[27.219:294](∅→∅),[27.219]→[27.219:294](∅→∅),[27.294]→[27.370:552](∅→∅),[27.552]→[27.446:470](∅→∅),[27.73497]→[27.446:470](∅→∅),[27.446]→[27.446:470](∅→∅),[27.560]→[27.560:568](∅→∅)
elselocal new_cursor_line = State.cursor1.linewhile new_cursor_line > 1 donew_cursor_line = new_cursor_line-1if State.lines[new_cursor_line].mode == 'text' thenState.cursor1.line = new_cursor_lineState.cursor1.pos = utf8.len(State.lines[State.cursor1.line].data) + 1breakendendelseif State.cursor1.line > 1 thenState.cursor1.line = State.cursor1.line-1State.cursor1.pos = utf8.len(State.lines[State.cursor1.line]) + 1 - replacement in text.lua at line 572
assert(State.lines[State.cursor1.line].mode == 'text')if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) thenif State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line]) then - replacement in text.lua at line 574[27.1002]→[27.738:745](∅→∅),[27.73906]→[27.738:745](∅→∅),[27.738]→[27.738:745](∅→∅),[27.745]→[27.1003:1097](∅→∅),[27.1097]→[27.827:869](∅→∅),[27.74015]→[27.827:869](∅→∅),[27.827]→[27.827:869](∅→∅),[27.869]→[27.1098:1231](∅→∅),[27.1231]→[27.984:1008](∅→∅),[27.74170]→[27.984:1008](∅→∅),[27.984]→[27.984:1008](∅→∅),[27.1101]→[27.1101:1109](∅→∅)
elselocal new_cursor_line = State.cursor1.linewhile new_cursor_line <= #State.lines-1 donew_cursor_line = new_cursor_line+1if State.lines[new_cursor_line].mode == 'text' thenState.cursor1.line = new_cursor_lineState.cursor1.pos = 1breakendendelseif State.cursor1.line <= #State.lines-1 thenState.cursor1.line = State.cursor1.line+1State.cursor1.pos = 1 - replacement in text.lua at line 599[27.2683]→[27.16712:16734](∅→∅),[27.2263]→[27.16712:16734](∅→∅),[27.16734]→[27.2292:2397](∅→∅),[27.2292]→[27.2292:2397](∅→∅),[27.2397]→[27.166:186](∅→∅),[27.6763]→[27.166:186](∅→∅),[27.74994]→[27.166:186](∅→∅),[27.166]→[27.166:186](∅→∅),[27.186]→[27.2398:2448](∅→∅),[27.2448]→[27.189:289](∅→∅),[27.222]→[27.2541:2589](∅→∅),[27.289]→[27.2541:2589](∅→∅),[27.2541]→[27.2541:2589](∅→∅),[27.2589]→[27.220:287](∅→∅),[27.6800]→[27.220:287](∅→∅),[27.75228]→[27.220:287](∅→∅),[27.220]→[27.220:287](∅→∅),[27.287]→[27.2590:2737](∅→∅),[27.2737]→[27.937:976](∅→∅),[27.976]→[27.402:408](∅→∅),[27.2737]→[27.402:408](∅→∅),[27.5183]→[27.402:408](∅→∅),[27.75411]→[27.402:408](∅→∅),[27.402]→[27.402:408](∅→∅),[27.408]→[27.2738:2861](∅→∅)
local y = State.topwhile State.cursor1.line <= #State.lines doif State.lines[State.cursor1.line].mode == 'text' thenbreakend--? print('cursor skips', State.cursor1.line)y = y + Drawing_padding_height + Drawing.pixels(State.lines[State.cursor1.line].h, State.width)State.cursor1.line = State.cursor1.line + 1end-- hack: insert a text line at bottom of file if necessaryif State.cursor1.line > #State.lines thenassert(State.cursor1.line == #State.lines+1)table.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 thenif State.top > App.screen.height - State.line_height then - replacement in text.lua at line 615[27.394]→[27.3259:3369](∅→∅),[27.3369]→[27.16735:16767](∅→∅),[27.1424]→[27.545:585](∅→∅),[27.3408]→[27.545:585](∅→∅),[27.16767]→[27.545:585](∅→∅),[27.76011]→[27.545:585](∅→∅),[27.545]→[27.545:585](∅→∅),[27.585]→[27.767:776](∅→∅),[27.767]→[27.767:776](∅→∅),[27.776]→[27.586:614](∅→∅),[27.614]→[27.3409:3466](∅→∅),[27.3466]→[27.665:767](∅→∅),[27.76076]→[27.665:767](∅→∅),[27.665]→[27.665:767](∅→∅),[27.767]→[27.290:387](∅→∅),[27.326]→[27.16768:16800](∅→∅),[27.387]→[27.16768:16800](∅→∅),[27.3557]→[27.16768:16800](∅→∅),[27.1458]→[27.857:946](∅→∅),[27.3596]→[27.857:946](∅→∅),[27.16800]→[27.857:946](∅→∅),[27.76227]→[27.857:946](∅→∅),[27.857]→[27.857:946](∅→∅)
if top2.screen_line > 1 or State.lines[top2.line-1].mode == 'text' thenlocal h = State.line_heightif y - h < State.top thenbreakendy = y - helseassert(top2.line > 1)assert(State.lines[top2.line-1].mode == 'drawing')-- 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 - hlocal h = State.line_heightif y - h < State.top thenbreak - edit in text.lua at line 619
y = y - h - replacement in text.lua at line 650[27.6001]→[27.1554:1648](∅→∅),[27.1554]→[27.1554:1648](∅→∅),[27.836]→[27.910:1037](∅→∅),[27.1648]→[27.910:1037](∅→∅),[27.910]→[27.910:1037](∅→∅)
local screen_line_starting_byte_offset = Text.offset(line.data, screen_line_starting_pos)--? print('iter', y, screen_line_index, screen_line_starting_pos, string.sub(line.data, screen_line_starting_byte_offset))local screen_line_starting_byte_offset = Text.offset(line, screen_line_starting_pos)--? print('iter', y, screen_line_index, screen_line_starting_pos, string.sub(line, screen_line_starting_byte_offset)) - replacement in text.lua at line 661
local s = string.sub(line.data, screen_line_starting_byte_offset)local s = string.sub(line, screen_line_starting_byte_offset) - replacement in text.lua at line 674
local start_offset = Text.offset(line.data, start_pos)local start_offset = Text.offset(line, start_pos) - replacement in text.lua at line 678
local past_end_offset = Text.offset(line.data, past_end_pos)screen_line = string.sub(line.data, start_offset, past_end_offset-1)local past_end_offset = Text.offset(line, past_end_pos)screen_line = string.sub(line, start_offset, past_end_offset-1) - replacement in text.lua at line 681
screen_line = string.sub(line.data, start_pos)screen_line = string.sub(line, start_pos) - edit in text.lua at line 784
if State.lines[loc1.line].mode == 'drawing' thenreturn {line=loc1.line, screen_line=1, screen_pos=1}end - edit in text.lua at line 845
elseif State.lines[loc2.line-1].mode == 'drawing' thenreturn {line=loc2.line-1, screen_line=1, screen_pos=1} - edit in text.lua at line 854
if line.mode ~= 'text' then return end - replacement in select.lua at line 56
local 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_offset = Text.offset(line, lo)local hi_offset = Text.offset(line, hi)local pos_offset = Text.offset(line, pos) - replacement in select.lua at line 63
local before = line.data:sub(pos_offset, lo_offset-1)local before = line:sub(pos_offset, lo_offset-1) - replacement in select.lua at line 68
local s = line.data:sub(lo_offset, hi_offset-1)local s = line:sub(lo_offset, hi_offset-1) - replacement in select.lua at line 95[27.1531]→[27.46343:46375](∅→∅),[27.79610]→[27.46343:46375](∅→∅),[27.46343]→[27.46343:46375](∅→∅),[27.46375]→[27.91:142](∅→∅),[27.142]→[27.212:283](∅→∅),[27.229]→[27.46471:46481](∅→∅),[27.283]→[27.46471:46481](∅→∅),[27.4292]→[27.46471:46481](∅→∅),[27.6090]→[27.46471:46481](∅→∅),[27.46471]→[27.46471:46481](∅→∅)
if line.mode == 'text' thenif Text.in_line(State, line_index, x,y) thenreturn line_index, Text.to_pos_on_line(State, line_index, x,y)endif Text.in_line(State, line_index, x,y) thenreturn line_index, Text.to_pos_on_line(State, line_index, x,y) - replacement in select.lua at line 139
local min_offset = Text.offset(State.lines[minl].data, minp)local max_offset = Text.offset(State.lines[maxl].data, maxp)local min_offset = Text.offset(State.lines[minl], minp)local max_offset = Text.offset(State.lines[maxl], maxp) - replacement in select.lua at line 143
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..State.lines[minl].data:sub(max_offset)State.lines[minl] = State.lines[minl]:sub(1, min_offset-1)..State.lines[minl]:sub(max_offset) - replacement in select.lua at line 147
local rhs = State.lines[maxl].data:sub(max_offset)local rhs = State.lines[maxl]:sub(max_offset) - replacement in select.lua at line 152
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..rhsState.lines[minl] = State.lines[minl]:sub(1, min_offset-1)..rhs - replacement in select.lua at line 168
local min_offset = Text.offset(State.lines[minl].data, minp)local max_offset = Text.offset(State.lines[maxl].data, maxp)local min_offset = Text.offset(State.lines[minl], minp)local max_offset = Text.offset(State.lines[maxl], maxp) - replacement in select.lua at line 171
return State.lines[minl].data:sub(min_offset, max_offset-1)return State.lines[minl]:sub(min_offset, max_offset-1) - replacement in select.lua at line 174
local result = {State.lines[minl].data:sub(min_offset)}local result = {State.lines[minl]:sub(min_offset)} - replacement in select.lua at line 176[27.48528]→[27.3569:3659](∅→∅),[27.100]→[27.48607:48615](∅→∅),[27.3659]→[27.48607:48615](∅→∅),[27.81719]→[27.48607:48615](∅→∅),[27.48607]→[27.48607:48615](∅→∅)
if State.lines[i].mode == 'text' thentable.insert(result, State.lines[i].data)endtable.insert(result, State.lines[i]) - replacement in select.lua at line 178
table.insert(result, State.lines[maxl].data:sub(1, max_offset-1))table.insert(result, State.lines[maxl]:sub(1, max_offset-1)) - replacement in search.lua at line 24
local pos = find(State.lines[State.cursor1.line].data, State.search_term, State.cursor1.pos)local pos = find(State.lines[State.cursor1.line], State.search_term, State.cursor1.pos) - replacement in search.lua at line 31
pos = find(State.lines[i].data, State.search_term)pos = find(State.lines[i], State.search_term) - replacement in search.lua at line 42
pos = find(State.lines[i].data, State.search_term)pos = find(State.lines[i], State.search_term) - replacement in search.lua at line 52
pos = find(State.lines[State.cursor1.line].data, State.search_term)pos = find(State.lines[State.cursor1.line], State.search_term) - replacement in search.lua at line 72
local pos = rfind(State.lines[State.cursor1.line].data, State.search_term, State.cursor1.pos-1)local pos = rfind(State.lines[State.cursor1.line], State.search_term, State.cursor1.pos-1) - replacement in search.lua at line 79
pos = rfind(State.lines[i].data, State.search_term)pos = rfind(State.lines[i], State.search_term) - replacement in search.lua at line 90
pos = rfind(State.lines[i].data, State.search_term)pos = rfind(State.lines[i], State.search_term) - replacement in search.lua at line 100
pos = rfind(State.lines[State.cursor1.line].data, State.search_term)pos = rfind(State.lines[State.cursor1.line], State.search_term) - replacement in main_tests.lua at line 41
check_eq(Editor_state.lines[1].data, 'abc', 'F - test_drop_file/lines:1')check_eq(Editor_state.lines[2].data, 'def', 'F - test_drop_file/lines:2')check_eq(Editor_state.lines[3].data, 'ghi', 'F - test_drop_file/lines:3')check_eq(Editor_state.lines[1], 'abc', 'F - test_drop_file/lines:1')check_eq(Editor_state.lines[2], 'def', 'F - test_drop_file/lines:2')check_eq(Editor_state.lines[3], 'ghi', 'F - test_drop_file/lines:3') - edit in main.lua at line 2
json = require 'json' - edit in main.lua at line 47
edit.fixup_cursor(Editor_state) - edit in main.lua at line 50
if Editor_state.cursor1.line > #Editor_state.lines or Editor_state.lines[Editor_state.cursor1.line].mode ~= 'text' thenedit.fixup_cursor(Editor_state)end - edit in main.lua at line 127
edit.fixup_cursor(Editor_state) - replacement in file.lua at line 15
if line == '```lines' then -- inflexible with whitespace since these files are always autogeneratedtable.insert(result, load_drawing(infile_next_line))elsetable.insert(result, {mode='text', data=line})endtable.insert(result, line) - replacement in file.lua at line 19
table.insert(result, {mode='text', data=''})table.insert(result, '') - replacement in file.lua at line 30[15.419]→[27.16090:16169](∅→∅),[27.16090]→[27.16090:16169](∅→∅),[27.16169]→[6.14:51](∅→∅),[6.51]→[27.16206:16214](∅→∅),[27.16206]→[27.16206:16214](∅→∅)
if line.mode == 'drawing' thenstore_drawing(outfile, line)elseoutfile:write(line.data, '\n')endoutfile:write(line, '\n') - edit in file.lua at line 35[27.16243]→[27.16243:16524](∅→∅),[27.16524]→[27.1072:1235](∅→∅),[27.1235]→[27.16586:16664](∅→∅),[27.16586]→[27.16586:16664](∅→∅),[27.16664]→[27.1236:1306](∅→∅),[27.1306]→[27.16664:16742](∅→∅),[27.16664]→[27.16664:16742](∅→∅),[27.16742]→[27.1307:1350](∅→∅),[27.1350]→[27.139:234](∅→∅),[27.16742]→[27.139:234](∅→∅),[27.234]→[27.16782:16825](∅→∅),[27.16782]→[27.16782:16825](∅→∅),[27.16825]→[27.1351:1379](∅→∅),[27.1379]→[27.16825:16899](∅→∅),[27.16825]→[27.16825:16899](∅→∅),[27.16899]→[27.1380:1434](∅→∅),[27.1434]→[27.16899:16971](∅→∅),[27.16899]→[27.16899:16971](∅→∅),[27.16971]→[27.1435:1472](∅→∅),[27.1472]→[27.16971:17060](∅→∅),[27.16971]→[27.16971:17060](∅→∅),[27.17060]→[27.10:57](∅→∅),[27.57]→[27.10:66](∅→∅),[27.57]→[27.235:288](∅→∅),[27.66]→[27.235:288](∅→∅),[27.1519]→[27.235:288](∅→∅),[27.17060]→[27.235:288](∅→∅),[27.288]→[27.17060:17287](∅→∅),[27.17060]→[27.17060:17287](∅→∅),[27.17287]→[6.52:98](∅→∅),[6.98]→[27.17333:17507](∅→∅),[27.17333]→[27.17333:17507](∅→∅),[27.17507]→[6.99:131](∅→∅),[6.131]→[27.289:384](∅→∅),[27.17539]→[27.289:384](∅→∅),[27.384]→[27.17579:17771](∅→∅),[27.17579]→[27.17579:17771](∅→∅),[27.17771]→[6.132:164](∅→∅),[6.164]→[27.17803:17842](∅→∅),[27.17803]→[27.17803:17842](∅→∅),[27.17842]→[6.165:281](∅→∅),[6.281]→[27.17958:17994](∅→∅),[27.17958]→[27.17958:17994](∅→∅),[27.17994]→[6.282:456](∅→∅),[6.456]→[27.67:123](∅→∅),[27.18168]→[27.67:123](∅→∅),[27.123]→[27.385:438](∅→∅),[27.18168]→[27.385:438](∅→∅),[27.438]→[27.18168:18207](∅→∅),[27.18168]→[27.18168:18207](∅→∅),[27.18207]→[27.1376:1381](∅→∅)
json = require 'json'function load_drawing(infile_next_line)local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}while true dolocal line = infile_next_line()assert(line)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.insert_point(drawing.points, shape.p1.x, shape.p1.y)drawing.points[shape.p1].name = namename = shape.p2.nameshape.p2 = Drawing.insert_point(drawing.points, shape.p2.x, shape.p2.y)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.insert_point(drawing.points, p.x,p.y)drawing.points[shape.vertices[i]].name = nameendelseif shape.mode == 'circle' or shape.mode == 'arc' thenlocal name = shape.center.nameshape.center = Drawing.insert_point(drawing.points, shape.center.x,shape.center.y)drawing.points[shape.center].name = nameelseif shape.mode == 'deleted' then-- ignoreelseprint(shape.mode)assert(false)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' thenoutfile:write(json.encode(shape), '\n')elseif shape.mode == 'line' or shape.mode == 'manhattan' thenlocal line = json.encode({mode=shape.mode, p1=drawing.points[shape.p1], p2=drawing.points[shape.p2]})outfile:write(line, '\n')elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' thenlocal obj = {mode=shape.mode, vertices={}}for _,p in ipairs(shape.vertices) dotable.insert(obj.vertices, drawing.points[p])endlocal line = json.encode(obj)outfile:write(line, '\n')elseif shape.mode == 'circle' thenoutfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius}), '\n')elseif shape.mode == 'arc' thenoutfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius, start_angle=shape.start_angle, end_angle=shape.end_angle}), '\n')elseif shape.mode == 'deleted' then-- ignoreelseprint(shape.mode)assert(false)endendoutfile:write('```\n')end - replacement in file.lua at line 43[27.1566]→[27.2583:2603](∅→∅),[27.2603]→[27.1566:1671](∅→∅),[27.1566]→[27.1566:1671](∅→∅),[27.1671]→[27.2604:2765](∅→∅),[27.2765]→[27.1740:1749](∅→∅),[27.1740]→[27.1740:1749](∅→∅),[27.1749]→[27.2766:2800](∅→∅),[27.2800]→[27.1749:1810](∅→∅),[27.1749]→[27.1749:1810](∅→∅)
--? print(line)if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated--? print('inserting drawing')i, drawing = load_drawing_from_array(next_line, a, i)--? print('i now', i)table.insert(result, drawing)else--? print('inserting text')table.insert(result, {mode='text', data=line})endtable.insert(result, line) - replacement in file.lua at line 46
table.insert(result, {mode='text', data=''})table.insert(result, '') - edit in file.lua at line 49[27.1910]→[27.1910:2106](∅→∅),[27.2106]→[27.2801:2818](∅→∅),[27.2818]→[27.2106:3102](∅→∅),[27.2106]→[27.2106:3102](∅→∅),[27.3102]→[27.58:105](∅→∅),[27.105]→[27.124:180](∅→∅),[27.105]→[27.3148:3255](∅→∅),[27.180]→[27.3148:3255](∅→∅),[27.3148]→[27.3148:3255](∅→∅),[27.3255]→[27.2819:2839](∅→∅)
endfunction load_drawing_from_array(iter, a, i)local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}local linewhile true doi, line = iter(a, i)assert(i)--? 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.insert_point(drawing.points, shape.p1.x, shape.p1.y)drawing.points[shape.p1].name = namename = shape.p2.nameshape.p2 = Drawing.insert_point(drawing.points, shape.p2.x, shape.p2.y)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.insert_point(drawing.points, p.x,p.y)drawing.points[shape.vertices[i]].name = nameendelseif shape.mode == 'circle' or shape.mode == 'arc' thenlocal name = shape.center.nameshape.center = Drawing.insert_point(drawing.points, shape.center.x,shape.center.y)drawing.points[shape.center].name = nameelseif shape.mode == 'deleted' then-- ignoreelseprint(shape.mode)assert(false)endtable.insert(drawing.shapes, shape)endreturn i, drawing - edit in edit.lua at line 4
Stroke_color = {r=0, g=0, b=0}Current_stroke_color = {r=0.7, g=0.7, b=0.7} -- in process of being drawnCurrent_name_background_color = {r=1, g=0, b=0, a=0.1} -- name currently being edited - edit in edit.lua at line 6
Icon_color = {r=0.7, g=0.7, b=0.7} -- color of current mode icon in drawingsHelp_color = {r=0, g=0.5, b=0}Help_background_color = {r=0, g=0.5, b=0, a=0.1} - edit in edit.lua at line 11[27.401]→[27.401:524](∅→∅),[27.524]→[27.15:103](∅→∅),[27.22248]→[27.15:103](∅→∅),[27.103]→[27.22248:22249](∅→∅),[27.22248]→[27.22248:22249](∅→∅)
Drawing_padding_top = 10Drawing_padding_bottom = 10Drawing_padding_height = Drawing_padding_top + Drawing_padding_bottomSame_point_distance = 4 -- pixel distance at which two points are considered the same - edit in edit.lua at line 15
require 'drawing'require 'geom'require 'help'require 'icons' - replacement in edit.lua at line 21
-- a line is either text or a drawing-- a text is a table with:-- mode = 'text',-- string data,-- a drawing is a table with:-- mode = 'drawing'-- a (y) coord in pixels (updated while painting screen),-- a (h)eight,-- an array of points, and-- an array of shapes-- a shape is a table containing:-- a mode-- an array points for mode 'freehand' (raw x,y coords; freehand drawings don't pollute the points array of a drawing)-- an array vertices for mode 'polygon', 'rectangle', 'square'-- p1, p2 for mode 'line'-- center, radius for mode 'circle'-- center, radius, start_angle, end_angle for mode 'arc'-- Unless otherwise specified, coord fields are normalized; a drawing is always 256 units wide-- The field names are carefully chosen so that switching modes in midstream-- remembers previously entered points where that makes sense.lines = {{mode='text', data=''}}, -- array of lineslines = {''}, -- array of strings - edit in edit.lua at line 58
current_drawing_mode = 'line',previous_drawing_mode = nil, -- extra state for some ephemeral modes like moving/deleting/naming points - edit in edit.lua at line 82[27.3868]→[27.89:123](∅→∅),[27.123]→[24.221:333](∅→∅),[24.333]→[27.347:366](∅→∅),[27.347]→[27.347:366](∅→∅)
function edit.fixup_cursor(State)for i,line in ipairs(State.lines) doif line.mode == 'text' thenState.cursor1.line = ibreakendendend - replacement in edit.lua at line 97[27.3138]→[27.15:91](∅→∅),[27.91]→[27.273:298](∅→∅),[27.298]→[27.92:143](∅→∅),[27.4442]→[27.92:143](∅→∅),[27.143]→[27.299:340](∅→∅),[27.340]→[27.189:609](∅→∅),[27.189]→[27.189:609](∅→∅),[27.609]→[27.1896:1966](∅→∅),[27.1966]→[27.609:972](∅→∅),[27.8494]→[27.609:972](∅→∅),[27.609]→[27.609:972](∅→∅),[27.972]→[27.5229:5239](∅→∅),[27.5229]→[27.5229:5239](∅→∅),[27.5239]→[27.341:419](∅→∅),[27.419]→[27.3585:3617](∅→∅),[27.1038]→[27.3585:3617](∅→∅),[27.3585]→[27.3585:3617](∅→∅),[27.3617]→[27.1039:1066](∅→∅),[27.1066]→[27.5308:5347](∅→∅),[27.3617]→[27.5308:5347](∅→∅),[27.99122]→[27.5308:5347](∅→∅),[27.5308]→[27.5308:5347](∅→∅),[27.5347]→[27.525:557](∅→∅),[27.557]→[13.15:56](∅→∅),[13.56]→[27.558:633](∅→∅),[27.47]→[27.558:633](∅→∅),[27.633]→[27.5483:5492](∅→∅),[27.684]→[27.5483:5492](∅→∅),[27.3725]→[27.5483:5492](∅→∅),[27.99244]→[27.5483:5492](∅→∅),[27.5483]→[27.5483:5492](∅→∅),[27.5492]→[27.1067:1110](∅→∅)
if line.mode == 'text' then--? print('text.draw', y, line_index)local startpos = 1if line_index == State.screen_top1.line thenstartpos = State.screen_top1.posendif line.data == '' then-- button to insert new drawingbutton('draw', {x=4,y=y+4, w=12,h=12, color={1,1,0},icon = icon.insert_drawing,onpress1 = function()Drawing.before = snapshot(State, line_index-1, line_index)table.insert(State.lines, line_index, {mode='drawing', y=y, h=256/2, points={}, shapes={}, pending={}})table.insert(State.line_cache, line_index, {})if State.cursor1.line >= line_index thenState.cursor1.line = State.cursor1.line+1endschedule_save(State)record_undo_event(State, {before=Drawing.before, after=snapshot(State, line_index-1, line_index+1)})end,})endy, State.screen_bottom1.pos = Text.draw(State, line_index, y, startpos)y = y + State.line_height--? print('=> y', y)elseif line.mode == 'drawing' theny = y+Drawing_padding_topDrawing.draw(State, line_index, y)y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottomelseprint(line.mode)assert(false)--? print('text.draw', y, line_index)local startpos = 1if line_index == State.screen_top1.line thenstartpos = State.screen_top1.pos - edit in edit.lua at line 102
y, State.screen_bottom1.pos = Text.draw(State, line_index, y, startpos)y = y + State.line_height--? print('=> y', y) - replacement in edit.lua at line 109
--? print('screen bottom: '..tostring(State.screen_bottom1.pos)..' in '..tostring(State.lines[State.screen_bottom1.line].data))--? print('screen bottom: '..tostring(State.screen_bottom1.pos)..' in '..tostring(State.lines[State.screen_bottom1.line])) - edit in edit.lua at line 116
Drawing.update(State, dt) - replacement in edit.lua at line 141[27.4696]→[27.7059:7091](∅→∅),[27.100424]→[27.7059:7091](∅→∅),[27.7059]→[27.7059:7091](∅→∅),[27.7091]→[27.153:204](∅→∅),[27.60]→[27.7173:7631](∅→∅),[27.204]→[27.7173:7631](∅→∅),[27.4791]→[27.7173:7631](∅→∅),[27.6356]→[27.7173:7631](∅→∅),[27.22676]→[27.7173:7631](∅→∅),[27.100533]→[27.7173:7631](∅→∅),[27.7173]→[27.7173:7631](∅→∅),[27.7631]→[10.15:82](∅→∅),[10.82]→[27.4792:4961](∅→∅),[27.7697]→[27.4792:4961](∅→∅),[27.4961]→[27.7830:7859](∅→∅),[27.100745]→[27.7830:7859](∅→∅),[27.7830]→[27.7830:7859](∅→∅),[27.7859]→[27.294:356](∅→∅),[27.117]→[27.7952:7962](∅→∅),[27.356]→[27.7952:7962](∅→∅),[27.5067]→[27.7952:7962](∅→∅),[27.6469]→[27.7952:7962](∅→∅),[27.22758]→[27.7952:7962](∅→∅),[27.100865]→[27.7952:7962](∅→∅),[27.7952]→[27.7952:7962](∅→∅),[27.7962]→[27.5068:5144](∅→∅),[27.5144]→[27.8026:8089](∅→∅),[27.100956]→[27.8026:8089](∅→∅),[27.8026]→[27.8026:8089](∅→∅),[27.8089]→[13.57:192](∅→∅),[13.192]→[27.5145:5243](∅→∅),[27.754]→[27.5145:5243](∅→∅),[27.8134]→[27.5145:5243](∅→∅),[27.5243]→[27.2242:2295](∅→∅),[27.2295]→[13.193:261](∅→∅),[27.77]→[27.8321:8345](∅→∅),[13.261]→[27.8321:8345](∅→∅),[27.8321]→[27.8321:8345](∅→∅)
if line.mode == 'text' thenif Text.in_line(State, line_index, x,y) then-- delicate dance between cursor, selection and old cursor/selection-- scenarios:-- regular press+release: sets cursor, clears selection-- shift press+release:-- sets selection to old cursor if not set otherwise leaves it untouched-- sets cursor-- press and hold to start a selection: sets selection on press, cursor on release-- press and hold, then press shift: ignore shift-- i.e. mouse_released should never look at shift stateState.old_cursor1 = State.cursor1State.old_selection1 = State.selection1State.mousepress_shift = App.shift_down()State.selection1 = {line=line_index,pos=Text.to_pos_on_line(State, line_index, x, y),}--? print('selection', State.selection1.line, State.selection1.pos)breakendelseif line.mode == 'drawing' thenlocal line_cache = State.line_cache[line_index]if Drawing.in_drawing(line, line_cache, x, y, State.left,State.right) thenState.lines.current_drawing_index = line_indexState.lines.current_drawing = lineDrawing.before = snapshot(State, line_index)Drawing.mouse_pressed(State, line_index, x,y, mouse_button)breakendif Text.in_line(State, line_index, x,y) then-- delicate dance between cursor, selection and old cursor/selection-- scenarios:-- regular press+release: sets cursor, clears selection-- shift press+release:-- sets selection to old cursor if not set otherwise leaves it untouched-- sets cursor-- press and hold to start a selection: sets selection on press, cursor on release-- press and hold, then press shift: ignore shift-- i.e. mouse_released should never look at shift stateState.old_cursor1 = State.cursor1State.old_selection1 = State.selection1State.mousepress_shift = App.shift_down()State.selection1 = {line=line_index,pos=Text.to_pos_on_line(State, line_index, x, y),}--? print('selection', State.selection1.line, State.selection1.pos)break - replacement in edit.lua at line 167[27.8468]→[27.5339:5377](∅→∅),[27.5377]→[27.15:68](∅→∅),[27.68]→[27.420:445](∅→∅),[27.445]→[27.8641:8668](∅→∅),[27.8641]→[27.8641:8668](∅→∅),[27.8668]→[27.2296:2410](∅→∅),[27.2410]→[27.8762:8804](∅→∅),[27.5478]→[27.8762:8804](∅→∅),[27.101270]→[27.8762:8804](∅→∅),[27.8762]→[27.8762:8804](∅→∅),[27.8804]→[27.5479:5529](∅→∅),[27.5529]→[27.8848:8882](∅→∅),[27.101328]→[27.8848:8882](∅→∅),[27.8848]→[27.8848:8882](∅→∅),[27.8882]→[27.205:258](∅→∅),[27.165]→[27.8966:9005](∅→∅),[27.258]→[27.8966:9005](∅→∅),[27.5626]→[27.8966:9005](∅→∅),[27.6573]→[27.8966:9005](∅→∅),[27.22831]→[27.8966:9005](∅→∅),[27.101439]→[27.8966:9005](∅→∅),[27.8966]→[27.8966:9005](∅→∅),[27.9005]→[27.5627:5655](∅→∅),[27.5655]→[27.9027:9058](∅→∅),[27.101475]→[27.9027:9058](∅→∅),[27.9027]→[27.9027:9058](∅→∅),[27.9058]→[27.357:421](∅→∅),[27.224]→[27.9153:9165](∅→∅),[27.421]→[27.9153:9165](∅→∅),[27.5763]→[27.9153:9165](∅→∅),[27.6688]→[27.9153:9165](∅→∅),[27.22915]→[27.9153:9165](∅→∅),[27.101597]→[27.9153:9165](∅→∅),[27.9153]→[27.9153:9165](∅→∅),[27.9165]→[27.5764:5978](∅→∅),[27.5978]→[27.9343:9360](∅→∅),[27.101854]→[27.9343:9360](∅→∅),[27.9343]→[27.9343:9360](∅→∅),[27.9360]→[27.5979:6033](∅→∅),[27.6033]→[27.9402:9432](∅→∅),[27.101923]→[27.9402:9432](∅→∅),[27.9402]→[27.9402:9432](∅→∅),[27.9432]→[27.6034:6202](∅→∅),[27.6202]→[27.9564:9594](∅→∅),[27.102134]→[27.9564:9594](∅→∅),[27.9564]→[27.9564:9594](∅→∅)
if State.lines.current_drawing thenDrawing.mouse_released(State, x,y, mouse_button)schedule_save(State)if Drawing.before thenrecord_undo_event(State, {before=Drawing.before, after=snapshot(State, State.lines.current_drawing_index)})Drawing.before = nilendelsefor line_index,line in ipairs(State.lines) doif line.mode == 'text' thenif Text.in_line(State, line_index, x,y) then--? print('reset selection')State.cursor1 = {line=line_index,pos=Text.to_pos_on_line(State, line_index, x, y),}--? print('cursor', State.cursor1.line, State.cursor1.pos)if State.mousepress_shift thenif State.old_selection1.line == nil thenState.selection1 = State.old_cursor1elseState.selection1 = State.old_selection1endendState.old_cursor1, State.old_selection1, State.mousepress_shift = nilif eq(State.cursor1, State.selection1) thenState.selection1 = {}endbreakfor line_index,line in ipairs(State.lines) doif Text.in_line(State, line_index, x,y) then--? print('reset selection')State.cursor1 = {line=line_index,pos=Text.to_pos_on_line(State, line_index, x, y),}--? print('cursor', State.cursor1.line, State.cursor1.pos)if State.mousepress_shift thenif State.old_selection1.line == nil thenState.selection1 = State.old_cursor1elseState.selection1 = State.old_selection1 - edit in edit.lua at line 182
State.old_cursor1, State.old_selection1, State.mousepress_shift = nilif eq(State.cursor1, State.selection1) thenState.selection1 = {}endbreak - edit in edit.lua at line 188
--? print('selection:', State.selection1.line, State.selection1.pos) - edit in edit.lua at line 189
--? print('selection:', State.selection1.line, State.selection1.pos) - edit in edit.lua at line 198[27.2117]→[27.6496:6547](∅→∅),[27.9975]→[27.6496:6547](∅→∅),[27.6547]→[27.2411:2481](∅→∅),[27.2481]→[27.6610:6658](∅→∅),[27.6610]→[27.6610:6658](∅→∅),[27.6658]→[27.10119:10201](∅→∅),[27.102626]→[27.10119:10201](∅→∅),[27.10119]→[27.10119:10201](∅→∅),[27.10201]→[27.2482:2586](∅→∅)
elseif State.current_drawing_mode == 'name' thenlocal before = snapshot(State, State.lines.current_drawing_index)local drawing = State.lines.current_drawinglocal p = drawing.points[drawing.pending.target_point]p.name = p.name..trecord_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)}) - edit in edit.lua at line 206
not State.lines.current_drawing and - replacement in edit.lua at line 313[27.2812]→[27.14470:14561](∅→∅),[27.9002]→[27.14470:14561](∅→∅),[27.105277]→[27.14470:14561](∅→∅),[27.14470]→[27.14470:14561](∅→∅),[27.14561]→[16.1250:1292](∅→∅),[16.1292]→[27.15:81](∅→∅),[27.14592]→[27.15:81](∅→∅),[27.81]→[27.14653:14679](∅→∅),[27.14653]→[27.14653:14679](∅→∅),[27.14679]→[27.2813:2865](∅→∅),[27.2865]→[27.15:60](∅→∅),[27.14724]→[27.15:60](∅→∅),[27.60]→[27.2866:2952](∅→∅),[27.2952]→[27.578:605](∅→∅),[27.14834]→[27.578:605](∅→∅),[27.605]→[27.14856:14922](∅→∅),[27.14856]→[27.14856:14922](∅→∅),[27.14922]→[27.9003:9044](∅→∅),[27.9044]→[27.14957:15043](∅→∅),[27.105326]→[27.14957:15043](∅→∅),[27.14957]→[27.14957:15043](∅→∅),[27.15043]→[27.9045:9096](∅→∅),[27.9096]→[27.15088:15118](∅→∅),[27.105385]→[27.15088:15118](∅→∅),[27.15088]→[27.15088:15118](∅→∅),[27.15118]→[27.9097:9200](∅→∅),[27.9200]→[27.15203:15212](∅→∅),[27.105510]→[27.15203:15212](∅→∅),[27.15203]→[27.15203:15212](∅→∅),[27.15212]→[27.2953:3025](∅→∅),[27.3025]→[27.9266:9316](∅→∅),[27.9266]→[27.9266:9316](∅→∅),[27.9316]→[27.15315:15429](∅→∅),[27.105640]→[27.15315:15429](∅→∅),[27.15315]→[27.15315:15429](∅→∅),[27.15429]→[27.3026:3134](∅→∅),[27.3134]→[27.15517:15745](∅→∅),[27.9411]→[27.15517:15745](∅→∅),[27.105742]→[27.15517:15745](∅→∅),[27.15517]→[27.15517:15745](∅→∅),[27.15745]→[27.3135:3243](∅→∅),[27.3243]→[27.15833:15851](∅→∅),[27.9506]→[27.15833:15851](∅→∅),[27.105844]→[27.15833:15851](∅→∅),[27.15833]→[27.15833:15851](∅→∅),[27.15851]→[27.606:631](∅→∅)
-- dispatch to drawing or textelseif App.mouse_down(1) or chord:sub(1,2) == 'C-' then-- DON'T reset line_cache.starty herelocal drawing_index, drawing = Drawing.current_drawing(State)if drawing_index thenlocal before = snapshot(State, drawing_index)Drawing.keychord_pressed(State, chord)record_undo_event(State, {before=before, after=snapshot(State, drawing_index)})schedule_save(State)endelseif chord == 'escape' and not App.mouse_down(1) thenfor _,line in ipairs(State.lines) doif line.mode == 'drawing' thenline.show_help = falseendendelseif State.current_drawing_mode == 'name' thenif chord == 'return' thenState.current_drawing_mode = State.previous_drawing_modeState.previous_drawing_mode = nilelselocal before = snapshot(State, State.lines.current_drawing_index)local drawing = State.lines.current_drawinglocal p = drawing.points[drawing.pending.target_point]if chord == 'escape' thenp.name = nilrecord_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)})elseif chord == 'backspace' thenlocal len = utf8.len(p.name)local byte_offset = Text.offset(p.name, len-1)if len == 1 then byte_offset = 0 endp.name = string.sub(p.name, 1, byte_offset)record_undo_event(State, {before=before, after=snapshot(State, State.lines.current_drawing_index)})endendschedule_save(State)-- dispatch to text - replacement in README.md at line 1
# Plain text with lines# An editor for plain text.Not very useful by itself, but it's a fork of [lines.love](http://akkartik.name/lines.html)that you can take in other directions besides line drawings, while easilysharing patches between forks. - edit in README.md at line 7
An editor for plain text where you can also seamlessly insert line drawings. - edit in README.md at line 10
http://akkartik.name/lines.html - replacement in README.md at line 17
$ zip -r /tmp/lines.love *.lua$ zip -r /tmp/text.love *.lua - replacement in README.md at line 20
By default, lines.love reads/writes the file `lines.txt` in your defaultBy default, it reads/writes the file `lines.txt` in your default - replacement in README.md at line 23
To open a different file, drop it on the lines.love window.To open a different file, drop it on the app window. - replacement in README.md at line 39
lines.love has been exclusively tested so far with a US keyboard layout. IfExclusively tested so far with a US keyboard layout. If - replacement in README.md at line 50
other ways. lines.love works well in all circumstances with files under50KB.other ways. Works well in all circumstances with files under 50KB. - replacement in README.md at line 83[27.1142]→[27.1142:1497](∅→∅),[27.1497]→[26.20:62](∅→∅),[26.62]→[27.1497:1528](∅→∅),[27.1497]→[27.1497:1528](∅→∅)
Updates to lines.love can be downloaded from the following mirrors in additionto the website above:* https://github.com/akkartik/lines.love* https://repo.or.cz/lines.love.git* https://codeberg.org/akkartik/lines.love* https://tildegit.org/akkartik/lines.love* https://git.tilde.institute/akkartik/lines.love* https://git.sr.ht/~akkartik/lines.love* https://notabug.org/akkartik/lines.love* https://pagure.io/lines.loveThis repo is a fork of lines.love at [http://akkartik.name/lines.html](http://akkartik.name/lines.html).Updates to it can be downloaded from the following mirrors: - replacement in README.md at line 86[27.1529]→[4.18:96](∅→∅),[4.96]→[27.1603:1609](∅→∅),[27.1603]→[27.1603:1609](∅→∅),[27.1609]→[27.18:197](∅→∅),[27.197]→[9.19:40](∅→∅)
Forks of lines.love are encouraged. If you show me your fork, I'll link to ithere.* https://github.com/akkartik/lines-polygon-experiment -- an experiment thatuses separate shortcuts for regular polygons. `ctrl+3` for triangles,`ctrl+4` for squares, etc.## Associated tools* https://codeberg.org/akkartik/text.love* https://repo.or.cz/text.love.git* https://tildegit.org/akkartik/text.love* https://git.tilde.institute/akkartik/text.love* https://git.sr.ht/~akkartik/text.love* https://notabug.org/akkartik/text.love* https://github.com/akkartik/text.love* https://pagure.io/text.love - replacement in README.md at line 95
* https://codeberg.org/akkartik/lines2md exports lines.love files to Markdownand (non-editable) SVG.Further forks are encouraged. If you show me your fork, I'll link to it here.