B:BD[
8.22445] → [
9.1951:1996]
B:BD[
11.9658] → [
12.6003:6003]
B:BD[
13.470] → [
5.26:113]
B:BD[
11.11328] → [
12.6653:6653]
B:BD[
10.9616] → [
11.11329:11507]
B:BD[
14.1870] → [
12.6654:6847]
B:BD[
15.5593] → [
16.170:199]
B:BD[
17.6985] → [
18.134:241]
B:BD[
19.6870] → [
3.26:36]
B:BD[
11.11602] → [
20.75:75]
∅:D[
21.756] → [
22.364:368]
B:BD[
23.258] → [
22.364:368]
B:BD[
24.7283] → [
21.756:756]
∅:D[
25.258] → [
24.7264:7283]
B:BD[
24.7264] → [
24.7264:7283]
B:BD[
24.7264] → [
25.234:258]
∅:D[
26.567] → [
24.7237:7264]
∅:D[
15.6186] → [
24.7237:7264]
B:BD[
24.7237] → [
24.7237:7264]
B:BD[
26.519] → [
15.6139:6186]
B:BD[
24.7099] → [
26.411:519]
∅:D[
25.233] → [
24.7075:7099]
B:BD[
24.7075] → [
24.7075:7099]
B:BD[
24.7075] → [
25.209:233]
∅:D[
26.410] → [
24.7048:7075]
∅:D[
27.880] → [
24.7048:7075]
B:BD[
24.7048] → [
24.7048:7075]
B:BD[
26.363] → [
27.835:880]
B:BD[
24.6915] → [
26.259:363]
∅:D[
25.208] → [
24.6891:6915]
B:BD[
24.6891] → [
24.6891:6915]
B:BD[
24.6891] → [
25.184:208]
∅:D[
26.258] → [
24.6864:6891]
∅:D[
15.6138] → [
24.6864:6891]
B:BD[
24.6864] → [
24.6864:6891]
B:BD[
26.210] → [
15.6091:6138]
∅:D[
27.834] → [
26.166:210]
B:BD[
26.166] → [
26.166:210]
B:BD[
26.119] → [
27.789:834]
B:BD[
24.6651] → [
26.15:119]
∅:D[
25.183] → [
24.6627:6651]
B:BD[
24.6627] → [
24.6627:6651]
B:BD[
24.6627] → [
25.159:183]
∅:D[
15.6090] → [
24.6600:6627]
B:BD[
24.6600] → [
24.6600:6627]
B:BD[
24.6528] → [
15.6021:6090]
∅:D[
15.6020] → [
24.6481:6528]
B:BD[
24.6481] → [
24.6481:6528]
B:BD[
24.6451] → [
15.5989:6020]
B:BD[
10.16047] → [
24.6450:6451]
∅:D[
24.6449] → [
10.16043:16047]
∅:D[
17.9642] → [
10.16043:16047]
B:BD[
10.16043] → [
10.16043:16047]
∅:D[
25.158] → [
24.6430:6449]
B:BD[
24.6430] → [
24.6430:6449]
B:BD[
24.6430] → [
25.134:158]
∅:D[
15.5988] → [
24.6403:6430]
B:BD[
24.6403] → [
24.6403:6430]
B:BD[
24.6268] → [
15.5854:5988]
∅:D[
15.5853] → [
24.6228:6268]
B:BD[
24.6228] → [
24.6228:6268]
B:BD[
24.6184] → [
15.5808:5853]
∅:D[
28.5729] → [
24.6183:6184]
B:BD[
24.6183] → [
24.6183:6184]
∅:D[
29.1119] → [
28.5645:5729]
B:BD[
28.5645] → [
28.5645:5729]
B:BD[
28.5600] → [
29.1075:1119]
∅:D[
29.1074] → [
28.5422:5600]
B:BD[
28.5422] → [
28.5422:5600]
B:BD[
28.5422] → [
29.1052:1074]
∅:D[
29.1051] → [
28.5400:5422]
B:BD[
28.5400] → [
28.5400:5422]
B:BD[
28.5400] → [
29.915:1051]
∅:D[
30.447] → [
28.5371:5400]
B:BD[
24.6146] → [
28.5371:5400]
∅:D[
31.329] → [
30.442:447]
B:BD[
30.442] → [
30.442:447]
B:BD[
30.337] → [
30.337:387]
B:BD[
24.6146] → [
30.175:264]
∅:D[
15.5807] → [
24.6141:6146]
B:BD[
17.9642] → [
24.6141:6146]
B:BD[
10.16001] → [
15.5759:5807]
∅:D[
15.5758] → [
10.15990:16001]
∅:D[
32.7139] → [
10.15990:16001]
B:BD[
10.15990] → [
10.15990:16001]
B:BD[
33.1400] → [
15.5720:5758]
B:BD[
10.15878] → [
33.1293:1400]
∅:D[
20.75] → [
10.15871:15878]
∅:D[
34.631] → [
10.15871:15878]
∅:D[
35.8798] → [
10.15871:15878]
∅:D[
11.11602] → [
10.15871:15878]
B:BD[
10.15871] → [
10.15871:15878]
B:BD[
36.2812] → [
11.11580:11602]
B:BD[
34.577] → [
36.2710:2812]
B:BD[
10.14368] → [
34.552:577]
∅:D[
37.1438] → [
10.14360:14368]
∅:D[
17.8913] → [
10.14360:14368]
∅:D[
8.23116] → [
10.14360:14368]
∅:D[
38.105181] → [
10.14360:14368]
B:BD[
10.14360] → [
10.14360:14368]
∅:D[
39.360] → [
8.23041:23116]
B:BD[
37.1331] → [
8.23041:23116]
B:BD[
10.14227] → [
39.315:360]
∅:D[
40.8706] → [
10.14209:14227]
B:BD[
10.14209] → [
10.14209:14227]
B:BD[
10.14176] → [
40.8666:8706]
∅:D[
40.8665] → [
10.14165:14176]
B:BD[
10.14165] → [
10.14165:14176]
B:BD[
10.14136] → [
40.8631:8665]
∅:D[
41.454] → [
10.14032:14136]
B:BD[
10.14032] → [
10.14032:14136]
B:BD[
36.2709] → [
41.407:454]
B:BD[
17.8812] → [
36.2661:2709]
B:BD[
10.13904] → [
17.8769:8812]
∅:D[
2.230] → [
10.13768:13904]
∅:D[
33.1249] → [
10.13768:13904]
∅:D[
17.8768] → [
10.13768:13904]
∅:D[
38.105015] → [
10.13768:13904]
B:BD[
10.13768] → [
10.13768:13904]
B:BD[
10.13689] → [
2.123:230]
∅:D[
34.551] → [
10.13660:13689]
B:BD[
10.13660] → [
10.13660:13689]
B:BD[
10.13640] → [
34.526:551]
∅:D[
41.406] → [
10.13632:13640]
B:BD[
10.13632] → [
10.13632:13640]
B:BD[
10.13602] → [
41.379:406]
∅:D[
42.3962] → [
10.13588:13602]
∅:D[
17.8682] → [
10.13588:13602]
∅:D[
8.23040] → [
10.13588:13602]
∅:D[
38.104922] → [
10.13588:13602]
B:BD[
10.13588] → [
10.13588:13602]
∅:D[
33.1158] → [
8.22975:23040]
B:BD[
17.8593] → [
8.22975:23040]
B:BD[
10.13432] → [
33.1051:1158]
∅:D[
41.378] → [
10.13395:13432]
B:BD[
10.13395] → [
10.13395:13432]
B:BD[
10.13365] → [
41.351:378]
∅:D[
42.3865] → [
10.13351:13365]
B:BD[
10.13351] → [
10.13351:13365]
∅:D[
33.1050] → [
42.3829:3865]
B:BD[
17.8507] → [
42.3829:3865]
∅:D[
43.657] → [
10.13212:13241]
B:BD[
10.13212] → [
10.13212:13241]
B:BD[
10.13212] → [
43.501:657]
∅:D[
34.525] → [
10.13189:13212]
B:BD[
10.13189] → [
10.13189:13212]
∅:D[
44.398] → [
34.498:525]
∅:D[
19.7066] → [
34.498:525]
B:BD[
10.13167] → [
34.498:525]
B:BD[
44.296] → [
44.296:398]
B:BD[
10.12904] → [
17.8226:8421]
∅:D[
36.2660] → [
10.12856:12904]
B:BD[
10.12856] → [
10.12856:12904]
∅:D[
33.942] → [
36.2624:2660]
B:BD[
17.8225] → [
36.2624:2660]
B:BD[
10.12746] → [
33.835:942]
∅:D[
34.497] → [
10.12709:12746]
B:BD[
10.12709] → [
10.12709:12746]
∅:D[
44.206] → [
34.470:497]
∅:D[
19.6968] → [
34.470:497]
B:BD[
10.12687] → [
34.470:497]
B:BD[
44.104] → [
44.104:206]
B:BD[
17.8139] → [
45.1967:2037]
B:BD[
10.12424] → [
17.7944:8139]
∅:D[
36.2623] → [
10.12375:12424]
B:BD[
10.12375] → [
10.12375:12424]
∅:D[
2.122] → [
36.2587:2623]
∅:D[
33.834] → [
36.2587:2623]
B:BD[
17.7943] → [
36.2587:2623]
B:BD[
10.12265] → [
2.15:122]
∅:D[
3.36] → [
10.12236:12265]
∅:D[
19.6870] → [
10.12236:12265]
B:BD[
10.12236] → [
10.12236:12265]
∅:D[
30.174] → [
19.6843:6870]
B:BD[
10.12214] → [
19.6843:6870]
B:BD[
10.12181] → [
30.133:174]
∅:D[
46.289] → [
10.12152:12181]
∅:D[
19.6842] → [
10.12152:12181]
B:BD[
10.12152] → [
10.12152:12181]
B:BD[
10.12086] → [
46.158:289]
∅:D[
19.6814] → [
10.12057:12086]
B:BD[
10.12057] → [
10.12057:12086]
∅:D[
30.73] → [
19.6787:6814]
B:BD[
17.7806] → [
19.6787:6814]
B:BD[
10.11991] → [
30.15:73]
∅:D[
3.25] → [
10.11962:11991]
∅:D[
17.7755] → [
10.11962:11991]
∅:D[
38.103883] → [
10.11962:11991]
B:BD[
10.11962] → [
10.11962:11991]
B:BD[
17.7755] → [
3.15:25]
B:BD[
17.7565] → [
47.25240:25412]
B:BD[
10.11787] → [
17.7538:7565]
∅:D[
48.2183] → [
10.11739:11787]
B:BD[
10.11739] → [
10.11739:11787]
B:BD[
10.11710] → [
48.2149:2183]
∅:D[
48.2148] → [
10.11680:11710]
B:BD[
10.11680] → [
10.11680:11710]
B:BD[
17.7537] → [
48.2118:2148]
B:BD[
10.11621] → [
17.7491:7537]
∅:D[
17.7490] → [
10.11589:11621]
∅:D[
38.103555] → [
10.11589:11621]
B:BD[
10.11589] → [
10.11589:11621]
B:BD[
10.11407] → [
17.7278:7460]
∅:D[
17.7277] → [
10.11370:11407]
∅:D[
38.103307] → [
10.11370:11407]
B:BD[
10.11370] → [
10.11370:11407]
B:BD[
17.7245] → [
17.7245:7277]
B:BD[
10.11296] → [
17.7185:7215]
∅:D[
19.6786] → [
10.11262:11296]
B:BD[
10.11262] → [
10.11262:11296]
B:BD[
17.7184] → [
19.6689:6786]
B:BD[
17.7046] → [
17.7046:7184]
B:BD[
10.11014] → [
17.6986:7016]
∅:D[
18.241] → [
10.10984:11014]
∅:D[
17.6985] → [
10.10984:11014]
∅:D[
38.102945] → [
10.10984:11014]
B:BD[
10.10984] → [
10.10984:11014]
B:BD[
10.10962] → [
17.6957:6985]
∅:D[
42.3828] → [
10.10956:10962]
∅:D[
17.6956] → [
10.10956:10962]
∅:D[
8.22974] → [
10.10956:10962]
∅:D[
38.102909] → [
10.10956:10962]
B:BD[
10.10956] → [
10.10956:10962]
∅:D[
49.325] → [
8.22916:22974]
∅:D[
50.363] → [
8.22916:22974]
∅:D[
51.401] → [
8.22916:22974]
∅:D[
43.500] → [
8.22916:22974]
B:BD[
10.10886] → [
8.22916:22974]
B:BD[
10.10752] → [
51.214:401]
∅:D[
17.6873] → [
10.10522:10752]
∅:D[
38.102812] → [
10.10522:10752]
B:BD[
10.10522] → [
10.10522:10752]
∅:D[
15.5675] → [
17.6800:6831]
B:BD[
17.6800] → [
17.6800:6831]
B:BD[
10.10343] → [
15.5627:5675]
∅:D[
34.469] → [
10.10338:10343]
B:BD[
10.10338] → [
10.10338:10343]
B:BD[
10.10320] → [
34.446:469]
∅:D[
52.162] → [
10.10314:10320]
∅:D[
53.273] → [
10.10314:10320]
∅:D[
54.806] → [
10.10314:10320]
∅:D[
35.8796] → [
10.10314:10320]
B:BD[
10.10314] → [
10.10314:10320]
B:BD[
35.8765] → [
35.8766:8796]
B:BD[
52.133] → [
35.8765:8765]
B:BD[
10.10292] → [
52.26:133]
∅:D[
36.2586] → [
10.10285:10292]
∅:D[
17.6749] → [
10.10285:10292]
∅:D[
38.102724] → [
10.10285:10292]
B:BD[
10.10285] → [
10.10285:10292]
B:BD[
17.6495] → [
48.2089:2117]
∅:D[
16.199] → [
17.6394:6467]
∅:D[
33.743] → [
17.6394:6467]
∅:D[
15.5593] → [
17.6394:6467]
B:BD[
17.6394] → [
17.6394:6467]
B:BD[
10.9696] → [
15.5558:5593]
∅:D[
11.11579] → [
10.9691:9696]
B:BD[
10.9691] → [
10.9691:9696]
∅:D[
14.2225] → [
10.9685:9691]
∅:D[
17.6276] → [
10.9685:9691]
∅:D[
38.102222] → [
10.9685:9691]
B:BD[
10.9685] → [
10.9685:9691]
∅:D[
55.212] → [
14.2163:2225]
B:BD[
14.2163] → [
14.2163:2225]
B:BD[
14.2163] → [
55.167:212]
∅:D[
56.182] → [
14.2080:2163]
B:BD[
14.2080] → [
14.2080:2163]
B:BD[
14.2073] → [
56.161:182]
∅:D[
55.166] → [
14.2014:2073]
B:BD[
14.2014] → [
14.2014:2073]
B:BD[
14.2014] → [
55.121:166]
∅:D[
57.2028] → [
14.1870:2014]
∅:D[
12.6847] → [
14.1870:2014]
B:BD[
14.1870] → [
14.1870:2014]
∅:D[
58.936] → [
14.1864:1870]
∅:D[
59.1241] → [
14.1864:1870]
∅:D[
9.3165] → [
14.1864:1870]
B:BD[
17.6276] → [
14.1864:1870]
∅:D[
11.11507] → [
10.9616:9624]
B:BD[
10.9616] → [
10.9616:9624]
∅:D[
20.73] → [
10.9594:9616]
∅:D[
12.6653] → [
10.9594:9616]
∅:D[
35.8763] → [
10.9594:9616]
∅:D[
11.11328] → [
10.9594:9616]
B:BD[
10.9594] → [
10.9594:9616]
∅:D[
12.6651] → [
11.11132:11328]
B:BD[
11.11132] → [
11.11132:11328]
B:BD[
11.11067] → [
12.6539:6651]
∅:D[
12.6538] → [
11.10948:11067]
B:BD[
11.10948] → [
11.10948:11067]
B:BD[
11.10913] → [
12.6459:6538]
∅:D[
20.71] → [
11.10816:10913]
∅:D[
12.6458] → [
11.10816:10913]
B:BD[
10.8468] → [
11.10816:10913]
B:BD[
17.5338] → [
12.6339:6458]
∅:D[
15.5504] → [
17.5299:5338]
B:BD[
17.5299] → [
17.5299:5338]
B:BD[
10.8364] → [
15.5450:5504]
∅:D[
9.2595] → [
10.8359:8364]
B:BD[
10.8359] → [
10.8359:8364]
B:BD[
10.8359] → [
9.2287:2595]
∅:D[
12.6338] → [
10.8345:8359]
∅:D[
35.8761] → [
10.8345:8359]
∅:D[
11.10815] → [
10.8345:8359]
B:BD[
10.8345] → [
10.8345:8359]
B:BD[
12.6324] → [
12.6325:6338]
B:BD[
11.10729] → [
12.6324:6324]
∅:D[
12.6322] → [
11.10473:10729]
B:BD[
11.10473] → [
11.10473:10729]
B:BD[
11.10408] → [
12.6181:6322]
B:BD[
17.4696] → [
11.9917:10408]
∅:D[
60.1281] → [
17.4648:4696]
B:BD[
10.7017] → [
17.4648:4696]
∅:D[
61.865] → [
60.1257:1281]
B:BD[
60.1257] → [
60.1257:1281]
B:BD[
60.1226] → [
61.793:865]
∅:D[
12.6180] → [
60.1049:1226]
B:BD[
10.7017] → [
60.1049:1226]
B:BD[
17.4581] → [
12.6075:6180]
∅:D[
27.721] → [
17.4542:4581]
B:BD[
17.4542] → [
17.4542:4581]
B:BD[
10.6757] → [
27.669:721]
∅:D[
5.113] → [
10.6746:6757]
∅:D[
13.470] → [
10.6746:6757]
∅:D[
17.4487] → [
10.6746:6757]
∅:D[
38.100241] → [
10.6746:6757]
B:BD[
10.6746] → [
10.6746:6757]
B:BD[
17.4441] → [
13.446:470]
B:BD[
10.6692] → [
17.4415:4441]
∅:D[
17.4414] → [
10.6653:6692]
B:BD[
10.6653] → [
10.6653:6692]
B:BD[
10.6632] → [
17.4388:4414]
∅:D[
34.419] → [
10.6621:6632]
∅:D[
62.895] → [
10.6621:6632]
∅:D[
38.100147] → [
10.6621:6632]
B:BD[
10.6621] → [
10.6621:6632]
B:BD[
34.312] → [
62.789:895]
B:BD[
10.6468] → [
34.249:312]
∅:D[
17.4387] → [
10.6457:6468]
∅:D[
38.99992] → [
10.6457:6468]
B:BD[
10.6457] → [
10.6457:6468]
∅:D[
13.445] → [
17.4361:4387]
B:BD[
17.4361] → [
17.4361:4387]
∅:D[
63.544] → [
13.421:445]
∅:D[
62.788] → [
13.421:445]
B:BD[
17.4315] → [
13.421:445]
B:BD[
17.4252] → [
63.483:544]
B:BD[
10.6095] → [
17.4220:4252]
∅:D[
64.64] → [
10.6090:6095]
B:BD[
10.6090] → [
10.6090:6095]
B:BD[
10.6090] → [
64.53:64]
∅:D[
65.2101] → [
10.6084:6090]
B:BD[
10.6084] → [
10.6084:6090]
B:BD[
17.4219] → [
65.2069:2101]
∅:D[
9.2148] → [
17.4191:4219]
∅:D[
66.8245] → [
17.4191:4219]
∅:D[
11.9916] → [
17.4191:4219]
B:BD[
17.4191] → [
17.4191:4219]
B:BD[
10.5923] → [
9.2108:2148]
∅:D[
17.4060] → [
10.5917:5923]
∅:D[
38.99635] → [
10.5917:5923]
B:BD[
10.5917] → [
10.5917:5923]
∅:D[
12.6074] → [
11.9765:9790]
B:BD[
11.9765] → [
11.9765:9790]
B:BD[
10.5853] → [
12.6004:6074]
∅:D[
68.13] → [
10.5845:5853]
∅:D[
69.1110] → [
10.5845:5853]
∅:D[
12.6003] → [
10.5845:5853]
∅:D[
11.9658] → [
10.5845:5853]
∅:D[
70.37331] → [
10.5845:5853]
B:BD[
10.5845] → [
10.5845:5853]
∅:D[
12.6001] → [
11.9505:9658]
∅:D[
70.37329] → [
11.9505:9658]
B:BD[
17.3138] → [
11.9505:9658]
B:BD[
17.3095] → [
12.5964:6001]
B:BD[
10.4249] → [
17.3031:3095]
∅:D[
17.3030] → [
10.4205:4249]
∅:D[
38.98451] → [
10.4205:4249]
B:BD[
10.4205] → [
10.4205:4249]
B:BD[
10.4124] → [
17.2931:3030]
∅:D[
9.1996] → [
10.4101:4124]
∅:D[
17.2930] → [
10.4101:4124]
∅:D[
8.22445] → [
10.4101:4124]
∅:D[
38.98330] → [
10.4101:4124]
B:BD[
10.4101] → [
10.4101:4124]
∅:D[
39.314] → [
8.22423:22445]
B:BD[
17.2901] → [
8.22423:22445]
B:BD[
71.1165] → [
39.268:314]
∅:D[
72.816] → [
71.988:1165]
B:BD[
45.1895] → [
71.988:1165]
B:BD[
73.1023] → [
72.642:816]
∅:D[
74.44] → [
73.999:1023]
∅:D[
17.2727] → [
73.999:1023]
B:BD[
10.3913] → [
73.999:1023]
∅:D[
64.52] → [
17.2701:2727]
∅:D[
75.366] → [
17.2701:2727]
B:BD[
10.3868] → [
17.2701:2727]
B:BD[
75.366] → [
64.28:52]
∅:D[
21.754] → [
76.790:795]
B:BD[
76.790] → [
76.790:795]
local screen_bottom1 = {line=nil, pos=nil}
-- give some time for the OS to flush everything to disk
love.timer.sleep(0.1)
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
break
--? print_and_log(('edit.mouse_release: finally selection %s,%s cursor %d,%d'):format(tostring(State.selection1.line), tostring(State.selection1.pos), State.cursor1.line, State.cursor1.pos))
--? print('text input', t)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
-- undo
end
edit.draw(State)
edit.update(State, 0)
App.screen.contents = {}
edit.mouse_release(State, x,y, mouse_button)
function edit.run_after_mouse_release(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.draw(State)
end
edit.update(State, 0)
App.screen.contents = {}
edit.mouse_press(State, x,y, mouse_button)
function edit.run_after_mouse_press(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.draw(State)
end
edit.update(State, 0)
App.screen.contents = {}
edit.mouse_release(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
function edit.run_after_mouse_click(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.draw(State)
end
edit.update(State, 0)
App.screen.contents = {}
edit.keychord_press(State, chord)
edit.key_release(State, chord)
function edit.run_after_keychord(State, chord)
-- not all keys are text_input
end
edit.draw(State)
edit.update(State, 0)
App.screen.contents = {}
function edit.run_after_text_input(State, t)
edit.keychord_press(State, t)
edit.text_input(State, t)
edit.key_release(State, t)
-- TODO: handle chords of multiple keys
-- all text_input events are also keypresses
14, -- font height assuming default LÖVE font
15) -- line height
end
App.screen.width - Test_margin_right,
function edit.initialize_test_state()
-- if you change these values, tests will start failing
return edit.initialize_state(
15, -- top margin
Test_margin_left,
Test_margin_right = 0
Test_margin_left = 25
-- Insulate tests from some key globals so I don't have to change the vast
-- majority of tests when they're modified for the real app.
--== some methods for tests
end
State.line_height = math.floor(font_height*1.3)
love.graphics.setFont(love.graphics.newFont(State.font_height))
function edit.update_font_settings(State, font_height)
State.font_height = font_height
end
function edit.key_release(State, key, scancode)
end
end
Text.keychord_press(State, chord)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
else
-- dispatch to text
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
schedule_save(State)
end
Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
if Text.cursor_out_of_screen(State) then
end
end
Text.insert_at_cursor(State, c)
else
Text.insert_return(State)
for _,code in utf8.codes(clipboard_data) do
local c = utf8.char(code)
if c == '\n' then
local clipboard_data = App.get_clipboard()
local before = snapshot(State, before_line)
local before_line = State.cursor1.line
-- We don't have a good sense of when to scroll, so we'll be conservative
-- and sometimes scroll when we didn't quite need to.
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
elseif chord == 'C-v' then
schedule_save(State)
end
App.set_clipboard(s)
if s then
local s = Text.cut_selection(State, State.left, State.right)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
end
elseif chord == 'C-x' then
App.set_clipboard(s)
if s then
local s = Text.selection(State)
elseif chord == 'C-c' then
elseif chord == 'C-a' then
State.selection1 = {line=1, pos=1}
State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1}
end
-- clipboard
schedule_save(State)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.before, event.after)
if event then
local src = event.after
local event = redo_event(State)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
end
elseif chord == 'C-y' then
schedule_save(State)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
patch_placeholders(State.line_cache, event.after, event.before)
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.after, event.before)
if event then
local src = event.before
local event = undo_event(State)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
elseif chord == 'C-z' then
Text.redraw_all(State)
edit.update_font_settings(State, 20)
elseif chord == 'C-0' then
if State.font_height > 2 then
edit.update_font_settings(State, State.font_height-2)
Text.redraw_all(State)
end
elseif chord == 'C--' then
Text.redraw_all(State)
edit.update_font_settings(State, State.font_height+2)
elseif chord == 'C-=' then
-- zoom
State.search_backup = {
cursor={line=State.cursor1.line, pos=State.cursor1.pos},
screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},
}
State.search_term = ''
end
return
elseif chord == 'C-f' then
Text.search_previous(State)
elseif chord == 'up' then
Text.search_next(State)
State.cursor1.pos = State.cursor1.pos+1
elseif chord == 'down' then
local len = utf8.len(State.search_term)
local byte_offset = Text.offset(State.search_term, len)
State.search_term = string.sub(State.search_term, 1, byte_offset-1)
elseif chord == 'backspace' then
State.search_backup = nil
State.search_term = nil
elseif chord == 'return' then
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
State.cursor1 = State.search_backup.cursor
State.screen_top1 = State.search_backup.screen_top
State.search_backup = nil
State.search_term = nil
if chord == 'escape' then
if State.search_term then
end
Text.delete_selection(State, State.left, State.right)
chord ~= 'C-a' and chord ~= 'C-c' and chord ~= 'C-x' and chord ~= 'backspace' and chord ~= 'delete' and chord ~= 'C-z' and chord ~= 'C-y' and not App.is_cursor_movement(chord) then
-- printable character created using shift key => delete selection
-- (we're not creating any ctrl-shift- or alt-shift- combinations using regular/printable keys)
(not App.shift_down() or utf8.len(key) == 1) and
if State.selection1.line and
function edit.keychord_press(State, chord, key)
end
schedule_save(State)
end
Text.text_input(State, t)
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
else
Text.search_next(State)
if State.search_term then
State.search_term = State.search_term..t
function edit.text_input(State, t)
end
end
for i=1,math.floor(-dy) do
Text.down(State)
end
edit.put_cursor_on_next_text_line(State)
State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
elseif dy < 0 then
for i=1,math.floor(dy) do
Text.up(State)
end
edit.put_cursor_on_next_text_line(State)
end
function edit.mouse_wheel_move(State, dx,dy)
if dy > 0 then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
end
end
end
end
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
--? print_and_log(('edit.mouse_release: cursor now %d,%d'):format(State.cursor1.line, State.cursor1.pos))
State.cursor1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
--? print_and_log(('edit.mouse_release: in line %d'):format(line_index))
for line_index,line in ipairs(State.lines) do
if Text.in_line(State, line_index, x,y) then
--? print_and_log(('edit.mouse_release(%d,%d): cursor at %d,%d'):format(x,y, State.cursor1.line, State.cursor1.pos))
if State.search_term then return end
function edit.mouse_release(State, x,y, mouse_button)
end
-- still here? click is below all screen lines
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=State.screen_bottom1.line,
pos=Text.pos_at_end_of_screen_line(State, State.screen_bottom1),
}
end
end
return
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
-- i.e. mouse_release should never look at shift state
--? print_and_log(('edit.mouse_press: in line %d'):format(line_index))
if 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
for line_index,line in ipairs(State.lines) do
}
return
end
line=State.screen_top1.line,
pos=State.screen_top1.pos,
if y < State.top then
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
--? print_and_log(('edit.mouse_press: cursor at %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if State.search_term then return end
function edit.mouse_press(State, x,y, mouse_button)
end
end
save_to_disk(State)
if State.next_save then
-- make sure to save before quitting
function edit.quit(State)
end
end
State.next_save = Current_time + 3 -- short enough that you're likely to still remember what you did
function schedule_save(State)
if State.next_save == nil then
end
end
State.next_save = nil
save_to_disk(State)
if State.next_save and State.next_save < Current_time then
function edit.update(State, dt)
end
return y
end
Text.draw_search_bar(State)
if State.search_term then
State.screen_bottom1 = screen_bottom1
end
--? print('=> y', y)
y, screen_bottom1.pos = Text.draw(State, line_index, y, startpos)
end
--? print('text.draw', y, line_index)
local startpos = 1
if line_index == State.screen_top1.line then
startpos = State.screen_top1.pos
screen_bottom1.line = line_index
if y + State.line_height > App.screen.height then break end
--? print('draw:', y, line_index, line)
for line_index = State.screen_top1.line,#State.lines do
local line = State.lines[line_index]
--? print('== draw')
local y = State.top
State.cursor_x = nil
State.cursor_y = nil
if not Text.le1(State.screen_top1, State.cursor1) then
print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
assert(false)
end
if #State.lines ~= #State.line_cache then
print(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))
assert(false)
end
App.color(Text_color)
function edit.draw(State)
-- return y drawn until
end
end
-- return y drawn until
function edit.draw(State)
App.color(Text_color)
if #State.lines ~= #State.line_cache then
print(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))
assert(false)
end
if not Text.le1(State.screen_top1, State.cursor1) then
print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
assert(false)
end
State.cursor_x = nil
State.cursor_y = nil
local y = State.top
local screen_bottom1 = {line=nil, pos=nil}
--? print('== draw')
for line_index = State.screen_top1.line,#State.lines do
local line = State.lines[line_index]
--? print('draw:', y, line_index, line)
if y + State.line_height > App.screen.height then break end
screen_bottom1.line = line_index
--? print('text.draw', y, line_index)
local startpos = 1
if line_index == State.screen_top1.line then
startpos = State.screen_top1.pos
end
y, screen_bottom1.pos = Text.draw(State, line_index, y, startpos)
--? print('=> y', y)
end
State.screen_bottom1 = screen_bottom1
if State.search_term then
Text.draw_search_bar(State)
end
return y
end
function edit.update(State, dt)
if State.next_save and State.next_save < Current_time then
save_to_disk(State)
State.next_save = nil
end
end
function schedule_save(State)
if State.next_save == nil then
State.next_save = Current_time + 3 -- short enough that you're likely to still remember what you did
end
end
function edit.quit(State)
-- make sure to save before quitting
if State.next_save then
save_to_disk(State)
-- give some time for the OS to flush everything to disk
love.timer.sleep(0.1)
end
end
function edit.mouse_press(State, x,y, mouse_button)
if State.search_term then return end
--? print_and_log(('edit.mouse_press: cursor at %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if y < State.top then
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=State.screen_top1.line,
pos=State.screen_top1.pos,
}
return
end
for line_index,line in ipairs(State.lines) do
if 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_release should never look at shift state
--? print_and_log(('edit.mouse_press: in line %d'):format(line_index))
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
return
end
end
-- still here? click is below all screen lines
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=State.screen_bottom1.line,
pos=Text.pos_at_end_of_screen_line(State, State.screen_bottom1),
}
end
function edit.mouse_release(State, x,y, mouse_button)
if State.search_term then return end
--? print_and_log(('edit.mouse_release(%d,%d): cursor at %d,%d'):format(x,y, State.cursor1.line, State.cursor1.pos))
for line_index,line in ipairs(State.lines) do
if Text.in_line(State, line_index, x,y) then
--? print_and_log(('edit.mouse_release: in line %d'):format(line_index))
State.cursor1 = {
line=line_index,
pos=Text.to_pos_on_line(State, line_index, x, y),
}
--? print_and_log(('edit.mouse_release: cursor now %d,%d'):format(State.cursor1.line, State.cursor1.pos))
if State.mousepress_shift then
if State.old_selection1.line == nil then
State.selection1 = State.old_cursor1
else
State.selection1 = State.old_selection1
end
end
State.old_cursor1, State.old_selection1, State.mousepress_shift = nil
if eq(State.cursor1, State.selection1) then
State.selection1 = {}
end
break
end
end
--? print_and_log(('edit.mouse_release: finally selection %s,%s cursor %d,%d'):format(tostring(State.selection1.line), tostring(State.selection1.pos), State.cursor1.line, State.cursor1.pos))
end
function edit.mouse_wheel_move(State, dx,dy)
if dy > 0 then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
for i=1,math.floor(dy) do
Text.up(State)
end
elseif dy < 0 then
State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
for i=1,math.floor(-dy) do
Text.down(State)
end
end
end
function edit.text_input(State, t)
--? print('text input', t)
if State.search_term then
State.search_term = State.search_term..t
Text.search_next(State)
else
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
Text.text_input(State, t)
end
schedule_save(State)
end
function edit.keychord_press(State, chord, key)
if State.selection1.line and
-- printable character created using shift key => delete selection
-- (we're not creating any ctrl-shift- or alt-shift- combinations using regular/printable keys)
(not App.shift_down() or utf8.len(key) == 1) and
chord ~= 'C-a' and chord ~= 'C-c' and chord ~= 'C-x' and chord ~= 'backspace' and chord ~= 'delete' and chord ~= 'C-z' and chord ~= 'C-y' and not App.is_cursor_movement(chord) then
Text.delete_selection(State, State.left, State.right)
end
if State.search_term then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
if chord == 'escape' then
State.search_term = nil
State.cursor1 = State.search_backup.cursor
State.screen_top1 = State.search_backup.screen_top
State.search_backup = nil
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
elseif chord == 'return' then
State.search_term = nil
State.search_backup = nil
elseif chord == 'backspace' then
local len = utf8.len(State.search_term)
local byte_offset = Text.offset(State.search_term, len)
State.search_term = string.sub(State.search_term, 1, byte_offset-1)
elseif chord == 'down' then
State.cursor1.pos = State.cursor1.pos+1
Text.search_next(State)
elseif chord == 'up' then
Text.search_previous(State)
end
return
elseif chord == 'C-f' then
State.search_term = ''
State.search_backup = {
cursor={line=State.cursor1.line, pos=State.cursor1.pos},
screen_top={line=State.screen_top1.line, pos=State.screen_top1.pos},
}
-- zoom
elseif chord == 'C-=' then
edit.update_font_settings(State, State.font_height+2)
Text.redraw_all(State)
elseif chord == 'C--' then
if State.font_height > 2 then
edit.update_font_settings(State, State.font_height-2)
Text.redraw_all(State)
end
elseif chord == 'C-0' then
edit.update_font_settings(State, 20)
Text.redraw_all(State)
-- undo
elseif chord == 'C-z' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
local event = undo_event(State)
if event then
local src = event.before
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.after, event.before)
patch_placeholders(State.line_cache, event.after, event.before)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
schedule_save(State)
end
elseif chord == 'C-y' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
local event = redo_event(State)
if event then
local src = event.after
State.screen_top1 = deepcopy(src.screen_top)
State.cursor1 = deepcopy(src.cursor)
State.selection1 = deepcopy(src.selection)
patch(State.lines, event.before, event.after)
-- if we're scrolling, reclaim all fragments to avoid memory leaks
Text.redraw_all(State)
schedule_save(State)
end
-- clipboard
elseif chord == 'C-a' then
State.selection1 = {line=1, pos=1}
State.cursor1 = {line=#State.lines, pos=utf8.len(State.lines[#State.lines].data)+1}
elseif chord == 'C-c' then
local s = Text.selection(State)
if s then
App.set_clipboard(s)
end
elseif chord == 'C-x' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
local s = Text.cut_selection(State, State.left, State.right)
if s then
App.set_clipboard(s)
end
schedule_save(State)
elseif chord == 'C-v' then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
-- We don't have a good sense of when to scroll, so we'll be conservative
-- and sometimes scroll when we didn't quite need to.
local before_line = State.cursor1.line
local before = snapshot(State, before_line)
local clipboard_data = App.get_clipboard()
for _,code in utf8.codes(clipboard_data) do
local c = utf8.char(code)
if c == '\n' then
Text.insert_return(State)
else
Text.insert_at_cursor(State, c)
end
end
if Text.cursor_out_of_screen(State) then
Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
end
schedule_save(State)
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
-- dispatch to text
else
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
Text.keychord_press(State, chord)
end
end
function edit.key_release(State, key, scancode)
end
function edit.update_font_settings(State, font_height)
State.font_height = font_height
love.graphics.setFont(love.graphics.newFont(State.font_height))
State.line_height = math.floor(font_height*1.3)
end
--== some methods for tests
-- Insulate tests from some key globals so I don't have to change the vast
-- majority of tests when they're modified for the real app.
Test_margin_left = 25
Test_margin_right = 0
function edit.initialize_test_state()
-- if you change these values, tests will start failing
return edit.initialize_state(
15, -- top margin
Test_margin_left,
App.screen.width - Test_margin_right,
14, -- font height assuming default LÖVE font
15) -- line height
end
-- all text_input events are also keypresses
-- TODO: handle chords of multiple keys
function edit.run_after_text_input(State, t)
edit.keychord_press(State, t)
edit.text_input(State, t)
edit.key_release(State, t)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
-- not all keys are text_input
function edit.run_after_keychord(State, chord)
edit.keychord_press(State, chord)
edit.key_release(State, chord)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_click(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_press(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end
function edit.run_after_mouse_release(State, x,y, mouse_button)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}
edit.update(State, 0)
edit.draw(State)
end