mirror of
https://github.com/protesilaos/dotfiles.git
synced 2026-09-10 07:16:20 -04:00
Compare commits
1041 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
722e27069c | ||
|
|
cd18b9f67a | ||
|
|
59d9078de9 | ||
|
|
77bb9978c2 | ||
|
|
89853cc302 | ||
|
|
f88e54b4f5 | ||
|
|
a662dbced5 | ||
|
|
cdf2c7c25d | ||
|
|
6e253caf22 | ||
|
|
4ac1f352f4 | ||
|
|
73b1b4afad | ||
|
|
4921e1c7f1 | ||
|
|
c31c524135 | ||
|
|
d52d443e08 | ||
|
|
03d39e0b66 | ||
|
|
05b70f3438 | ||
|
|
fcd246c3d4 | ||
|
|
d4aba6b5ff | ||
|
|
0dd7c0b40b | ||
|
|
9d269ae5ae | ||
|
|
43d549d985 | ||
|
|
971029cda0 | ||
|
|
7d0fa5668e | ||
|
|
1aad9f27da | ||
|
|
d9026d2d27 | ||
|
|
5606dcc95f | ||
|
|
6d8a394f37 | ||
|
|
904a770d31 | ||
|
|
536b15c5f6 | ||
|
|
6378548705 | ||
|
|
421f8fa5a1 | ||
|
|
2840a43cd1 | ||
|
|
08a4ca48b3 | ||
|
|
6126f1ba44 | ||
|
|
3ac5eafdf7 | ||
|
|
bf391c6eb1 | ||
|
|
49a825ca3e | ||
|
|
ccfe49b043 | ||
|
|
1c4cce305e | ||
|
|
d332c08c28 | ||
|
|
9069f5e88b | ||
|
|
0f3cdd9bf7 | ||
|
|
aeb94bc513 | ||
|
|
007fa0c1ec | ||
|
|
7dd11916e8 | ||
|
|
0c4d928bc4 | ||
|
|
6f85a2eb17 | ||
|
|
ceb013bf88 | ||
|
|
dee0a8eeb9 | ||
|
|
ef03df0e76 | ||
|
|
5e1c88ec50 | ||
|
|
48dd11f1d3 | ||
|
|
d6e9727316 | ||
|
|
a0702b24d5 | ||
|
|
3f2909a969 | ||
|
|
b6fca99207 | ||
|
|
d33751d21a | ||
|
|
dd2fa24e51 | ||
|
|
ef98eaa3d4 | ||
|
|
674a110740 | ||
|
|
3cadbe03c8 | ||
|
|
4dc4412c6e | ||
|
|
3d2dd92060 | ||
|
|
143af9d42f | ||
|
|
bc7ad395f6 | ||
|
|
4b5d83b693 | ||
|
|
6d4b0527a5 | ||
|
|
3e9833373f | ||
|
|
a89769f0cd | ||
|
|
1346cce3a7 | ||
|
|
d22a35fa40 | ||
|
|
a43d806a74 | ||
|
|
a18327ff07 | ||
|
|
f81b2884d2 | ||
|
|
2340545780 | ||
|
|
606d3104b9 | ||
|
|
68427506c9 | ||
|
|
2ae621365c | ||
|
|
3fee36dab6 | ||
|
|
fa91c8933d | ||
|
|
d539d16e41 | ||
|
|
36844d4ba6 | ||
|
|
109919ced3 | ||
|
|
e14bfda473 | ||
|
|
6d6dc50588 | ||
|
|
01913d7354 | ||
|
|
504bf1ea43 | ||
|
|
9826c77f43 | ||
|
|
b959a72a52 | ||
|
|
27e520da4e | ||
|
|
796bc6154c | ||
|
|
4c0232374b | ||
|
|
e7f01f70e3 | ||
|
|
c0cb7dba22 | ||
|
|
8e24cc6c14 | ||
|
|
2bbafcbe0f | ||
|
|
805ab6c121 | ||
|
|
5112e1a074 | ||
|
|
9e0e54711c | ||
|
|
c464b61796 | ||
|
|
7ec1eea77e | ||
|
|
501bf58842 | ||
|
|
ea29f7801e | ||
|
|
f301fefb0e | ||
|
|
d74027c0df | ||
|
|
f0ed0454bb | ||
|
|
8a3066ed3d | ||
|
|
ba75ec9625 | ||
|
|
2e20636f6a | ||
|
|
230d72c73f | ||
|
|
48c0b8b15b | ||
|
|
a214127f22 | ||
|
|
20dd62605e | ||
|
|
726be3d6ed | ||
|
|
a31308fdf6 | ||
|
|
0c67e27e4d | ||
|
|
fe2c236afb | ||
|
|
6d5fea8b7f | ||
|
|
3c1dc8bb6e | ||
|
|
73211a9fb9 | ||
|
|
f60b02dfcd | ||
|
|
eae08a6e10 | ||
|
|
200681077f | ||
|
|
f72ff38ff0 | ||
|
|
edbcc4a643 | ||
|
|
548bc34c2a | ||
|
|
67500c5352 | ||
|
|
d1c7291c38 | ||
|
|
5a8a41ebff | ||
|
|
6c68acf83c | ||
|
|
051503b0a9 | ||
|
|
7b2ed2f0f4 | ||
|
|
1a850788e4 | ||
|
|
ff673029ae | ||
|
|
2d2d2cf53a | ||
|
|
86afcc9d72 | ||
|
|
49c8160642 | ||
|
|
0cd445395b | ||
|
|
9b3b09c72a | ||
|
|
7189014484 | ||
|
|
340734653e | ||
|
|
f77dde215e | ||
|
|
407203c77d | ||
|
|
96d51ca6b3 | ||
|
|
89ad48e801 | ||
|
|
2cf01a8e54 | ||
|
|
984e15903e | ||
|
|
691262dfe5 | ||
|
|
08e8efe0de | ||
|
|
643968e197 | ||
|
|
cb325dcfe4 | ||
|
|
77044008d6 | ||
|
|
691f47ae9e | ||
|
|
3dd18f9bb1 | ||
|
|
27ebe1670b | ||
|
|
189df059a6 | ||
|
|
559be1a5ff | ||
|
|
9c1fe22810 | ||
|
|
cf7c37f81c | ||
|
|
72461ab316 | ||
|
|
455df21f30 | ||
|
|
799f475c7c | ||
|
|
96548fa23e | ||
|
|
92598a96af | ||
|
|
8063a7ff6a | ||
|
|
c09c71cacf | ||
|
|
8e5bc15205 | ||
|
|
1c7aa37d56 | ||
|
|
e1085c530d | ||
|
|
b1af3c87de | ||
|
|
05eacab88c | ||
|
|
f105f57b6a | ||
|
|
49d0038dbf | ||
|
|
5dcff0411d | ||
|
|
627a8fe266 | ||
|
|
295660e79c | ||
|
|
772ed185fc | ||
|
|
d3532eb303 | ||
|
|
d361c6f7be | ||
|
|
f567716412 | ||
|
|
7de2868d6f | ||
|
|
655bfd998a | ||
|
|
3fce2dd5d1 | ||
|
|
b35b471243 | ||
|
|
9a73b1a3cb | ||
|
|
57fddc174b | ||
|
|
b8b314038c | ||
|
|
e9755f0104 | ||
|
|
d81db7ca3f | ||
|
|
2ea544a0f7 | ||
|
|
3b67ebadff | ||
|
|
6533c1e82a | ||
|
|
2de225373b | ||
|
|
67eee8d7c2 | ||
|
|
52a84228a5 | ||
|
|
0bd21cecba | ||
|
|
35347c2646 | ||
|
|
57c7a6906a | ||
|
|
1921a50666 | ||
|
|
c10ea2bdd2 | ||
|
|
83aaa5aab3 | ||
|
|
56f1947a25 | ||
|
|
3b8652a10e | ||
|
|
f3ccaf2bef | ||
|
|
effcd2e544 | ||
|
|
5e69cce6db | ||
|
|
d58f691a19 | ||
|
|
9925a833d9 | ||
|
|
3e400f3812 | ||
|
|
41aaea3ba3 | ||
|
|
c0a1d4bb26 | ||
|
|
f550a6dca0 | ||
|
|
0aa51aea71 | ||
|
|
83d2483cec | ||
|
|
8c12ec0568 | ||
|
|
dcf744d223 | ||
|
|
24913de72b | ||
|
|
389bce85cf | ||
|
|
b663eee4e1 | ||
|
|
f2ab651213 | ||
|
|
d44128c794 | ||
|
|
51e041fb99 | ||
|
|
697ba95fae | ||
|
|
27fcb38ffc | ||
|
|
106af581d8 | ||
|
|
4a377fe019 | ||
|
|
3ae02b32f8 | ||
|
|
bdf5688bc5 | ||
|
|
c90065e04b | ||
|
|
0016ead2b8 | ||
|
|
a5eb73dd6d | ||
|
|
7be95d29f3 | ||
|
|
73191d7516 | ||
|
|
2eb43ac677 | ||
|
|
73fa300def | ||
|
|
168845fc26 | ||
|
|
d7453a9a45 | ||
|
|
01bb3f4cfc | ||
|
|
37e0ab2689 | ||
|
|
e4eff354fc | ||
|
|
d3fad85909 | ||
|
|
51bad82655 | ||
|
|
40696463c8 | ||
|
|
9a7bc2f6cb | ||
|
|
c8fa11267f | ||
|
|
25b77affa2 | ||
|
|
17529881f5 | ||
|
|
d682ef7eee | ||
|
|
88c47b85a2 | ||
|
|
59116892fd | ||
|
|
5a822f873f | ||
|
|
036388c739 | ||
|
|
1037b8ab8e | ||
|
|
561e51ad78 | ||
|
|
8096123e35 | ||
|
|
22adf95dea | ||
|
|
86cd15cdd4 | ||
|
|
08ed2d4bcf | ||
|
|
e59d96b71c | ||
|
|
a1a7f9101a | ||
|
|
cea7b6e111 | ||
|
|
abc737d84c | ||
|
|
4a94b3a81d | ||
|
|
357c29ba34 | ||
|
|
39edb50e2f | ||
|
|
b2c4daf178 | ||
|
|
0b4c9edfcd | ||
|
|
5909fd6dd2 | ||
|
|
4ee00e6de2 | ||
|
|
8d5909b77c | ||
|
|
f6a423159f | ||
|
|
108a81175a | ||
|
|
b4acab0624 | ||
|
|
3753e9e3f5 | ||
|
|
8f7b2a1b76 | ||
|
|
230d2d0795 | ||
|
|
d376ac7d96 | ||
|
|
befb4e8ff2 | ||
|
|
fd8c4679de | ||
|
|
32f26e11d0 | ||
|
|
96a0fce25c | ||
|
|
ba847a636f | ||
|
|
219d54cbf5 | ||
|
|
457fdf6ee0 | ||
|
|
715cc8cf2e | ||
|
|
ec42c4b637 | ||
|
|
dad0eff2a1 | ||
|
|
440618744a | ||
|
|
3f5c1c2376 | ||
|
|
93c2efffca | ||
|
|
39975bea83 | ||
|
|
c065ef8013 | ||
|
|
47fb6d8b72 | ||
|
|
ac547b252f | ||
|
|
b2aefa8931 | ||
|
|
ec481ce0af | ||
|
|
11ba02773e | ||
|
|
d284cc3ec0 | ||
|
|
7139025471 | ||
|
|
294c56c979 | ||
|
|
9b3e48d6af | ||
|
|
1d01db4d16 | ||
|
|
3b7e2373be | ||
|
|
c33581bd8f | ||
|
|
8a84e58d93 | ||
|
|
9089ee5355 | ||
|
|
bb6a4526cb | ||
|
|
4fbd7ad9b7 | ||
|
|
7d4b666302 | ||
|
|
fb68065d89 | ||
|
|
77285c8eec | ||
|
|
5a03195153 | ||
|
|
8a425233e3 | ||
|
|
cc5032df7a | ||
|
|
de6b7c42cf | ||
|
|
dcc1e0723a | ||
|
|
2128328c70 | ||
|
|
7f92e14a32 | ||
|
|
c32043cf34 | ||
|
|
fcd66b48cb | ||
|
|
c89ec43259 | ||
|
|
5e02e72023 | ||
|
|
4e810c7e95 | ||
|
|
11059eb5ec | ||
|
|
12843be87c | ||
|
|
709997007d | ||
|
|
2e1b781723 | ||
|
|
4db167e261 | ||
|
|
81858f8329 | ||
|
|
16d22ac49f | ||
|
|
f38d808707 | ||
|
|
a8dea72bf9 | ||
|
|
647c85c8bc | ||
|
|
feeef6d99c | ||
|
|
c2978f2f7c | ||
|
|
266928d65b | ||
|
|
91283c648a | ||
|
|
1bc651b600 | ||
|
|
338054d1e4 | ||
|
|
2ca13eba98 | ||
|
|
d1fc98559a | ||
|
|
eb709a302d | ||
|
|
aed4782a43 | ||
|
|
6da74c79a3 | ||
|
|
d8a0d4f811 | ||
|
|
0cb387926f | ||
|
|
583bb60e8b | ||
|
|
9504718fd7 | ||
|
|
a2f9c302f2 | ||
|
|
1c8a166aaa | ||
|
|
69ddfee51c | ||
|
|
5f5f2a2f58 | ||
|
|
572d39200b | ||
|
|
3ee0c668cc | ||
|
|
bdbcf731f3 | ||
|
|
639061da76 | ||
|
|
238a7a04d9 | ||
|
|
d4eb5916b0 | ||
|
|
118c2d6781 | ||
|
|
c6d236d88f | ||
|
|
7f13fbe6a1 | ||
|
|
e55b11035f | ||
|
|
207f425eb4 | ||
|
|
6a90ec55e6 | ||
|
|
d77347f84e | ||
|
|
037571bc95 | ||
|
|
04fb4ce1f5 | ||
|
|
6235fb1abe | ||
|
|
88875a3082 | ||
|
|
f017710070 | ||
|
|
545f9aaa51 | ||
|
|
70810c629d | ||
|
|
24b5855cec | ||
|
|
e9e7457acc | ||
|
|
1853d1e8ad | ||
|
|
63d25142af | ||
|
|
6d60de0847 | ||
|
|
3892f3e078 | ||
|
|
69d06fd066 | ||
|
|
e0ff3b3bae | ||
|
|
02c19b2c13 | ||
|
|
2a72a56c3d | ||
|
|
a3f396a9ab | ||
|
|
1fb362e22b | ||
|
|
ea09222488 | ||
|
|
7d86e9641d | ||
|
|
8f23150cfb | ||
|
|
36454ec12c | ||
|
|
6c4800ef0f | ||
|
|
2233458a40 | ||
|
|
c510b8742f | ||
|
|
0c48e7e5a0 | ||
|
|
48b66007fd | ||
|
|
4c1846ce84 | ||
|
|
80496c781e | ||
|
|
971bc2bed7 | ||
|
|
f6cc3e2cfc | ||
|
|
52e904d762 | ||
|
|
692d79df1b | ||
|
|
632e27544d | ||
|
|
283716ab71 | ||
|
|
7f6a6f6ed9 | ||
|
|
c8323a9bf4 | ||
|
|
3170d1299f | ||
|
|
d073448862 | ||
|
|
ca0e2abfef | ||
|
|
3e49eaceed | ||
|
|
313020dfe3 | ||
|
|
0a23c754d1 | ||
|
|
279164b6d4 | ||
|
|
bb357e3c83 | ||
|
|
626e092f19 | ||
|
|
1e3505e52a | ||
|
|
f14f12ea57 | ||
|
|
764ccb50be | ||
|
|
444ba41946 | ||
|
|
6a514d0ed3 | ||
|
|
2018105225 | ||
|
|
9de641952c | ||
|
|
82f20c2c51 | ||
|
|
a0f1c743d0 | ||
|
|
cf8506c9f3 | ||
|
|
828c141916 | ||
|
|
eb8b09676e | ||
|
|
77562dac15 | ||
|
|
090c0c2529 | ||
|
|
b2b1fe8318 | ||
|
|
5242398681 | ||
|
|
54157d3b57 | ||
|
|
66831920cf | ||
|
|
c60d4abd08 | ||
|
|
8b81b7201c | ||
|
|
539b2de37d | ||
|
|
fa6a0ad5fb | ||
|
|
c779d34fcf | ||
|
|
c569394ceb | ||
|
|
7983aa2f93 | ||
|
|
e7911b9da1 | ||
|
|
af5fa0cd68 | ||
|
|
564f5f075d | ||
|
|
eae0a6dc00 | ||
|
|
92a1d1d8d1 | ||
|
|
07bc2bc009 | ||
|
|
38ec49950a | ||
|
|
40ba0f5af2 | ||
|
|
7610a9132c | ||
|
|
cc4089023e | ||
|
|
6558303f45 | ||
|
|
6e5b083f35 | ||
|
|
4ede8e09a2 | ||
|
|
fcb490c15d | ||
|
|
53efa5524a | ||
|
|
5c17562d50 | ||
|
|
2cf0ee25af | ||
|
|
81a250b2af | ||
|
|
82db0b17c8 | ||
|
|
d6ab3d6391 | ||
|
|
6d308bd741 | ||
|
|
d18bb05db4 | ||
|
|
8d9669fa72 | ||
|
|
367c8bd74a | ||
|
|
c9c0ff9fad | ||
|
|
f8bb046db9 | ||
|
|
eef91be1a8 | ||
|
|
d4a71d7147 | ||
|
|
995c4973f7 | ||
|
|
da9a1746ca | ||
|
|
73972a1baf | ||
|
|
1f4a576386 | ||
|
|
9cdfd710cf | ||
|
|
49cb449c6d | ||
|
|
1cf6d288e3 | ||
|
|
10f4e8ea83 | ||
|
|
4879ca1dd0 | ||
|
|
fd97d2cd65 | ||
|
|
8294ad7cf7 | ||
|
|
641cc998d4 | ||
|
|
27f3626283 | ||
|
|
ae02f3df79 | ||
|
|
120cfc8d05 | ||
|
|
b6bae95d9a | ||
|
|
cd13d9db4f | ||
|
|
d82383edeb | ||
|
|
7fa7347cb3 | ||
|
|
a7735a0ebb | ||
|
|
eda58b87db | ||
|
|
0c22c4b3b5 | ||
|
|
7994421309 | ||
|
|
22b968dfdb | ||
|
|
aa0003e3cf | ||
|
|
fb2a8a8c1e | ||
|
|
1219c9ac73 | ||
|
|
03ae3b496d | ||
|
|
3fd151c2aa | ||
|
|
f0523f2b36 | ||
|
|
38be9ef381 | ||
|
|
0f7e3d7331 | ||
|
|
9575f0a1dc | ||
|
|
f8f5cb7549 | ||
|
|
f3b86df15b | ||
|
|
32102528a7 | ||
|
|
59bf35160b | ||
|
|
c2dbbabcea | ||
|
|
543ffa5dea | ||
|
|
83b595c848 | ||
|
|
93ce4a91eb | ||
|
|
f0e3214616 | ||
|
|
af1da0575f | ||
|
|
f34635ad84 | ||
|
|
04b1016f10 | ||
|
|
f365606d6d | ||
|
|
434160ecae | ||
|
|
3d5b125c7b | ||
|
|
6f67eebe61 | ||
|
|
98273390a4 | ||
|
|
9c4751310c | ||
|
|
cef5d6ed6e | ||
|
|
13dd210bc6 | ||
|
|
0d26d2efc1 | ||
|
|
fd7d7a7df5 | ||
|
|
bacf0b8d24 | ||
|
|
ae414f5a01 | ||
|
|
f84b1ffc42 | ||
|
|
be0f440349 | ||
|
|
6bad8a36e0 | ||
|
|
94cc5f190f | ||
|
|
b9f59a5c53 | ||
|
|
d71937be0d | ||
|
|
5bde162c02 | ||
|
|
6e558d9692 | ||
|
|
31eba792a0 | ||
|
|
a42cb7b6f1 | ||
|
|
d852d8c1e7 | ||
|
|
a16e7dbf39 | ||
|
|
1622f25243 | ||
|
|
e2117aaa30 | ||
|
|
c80e9ffc20 | ||
|
|
233ec940d2 | ||
|
|
1aa17d616a | ||
|
|
b94ec1f190 | ||
|
|
f64b5e666a | ||
|
|
10eb411c85 | ||
|
|
77912e6390 | ||
|
|
efe77c1c7e | ||
|
|
a64bae8b78 | ||
|
|
c29c221e49 | ||
|
|
500e863e5a | ||
|
|
6cc746439d | ||
|
|
8aa9e0db9b | ||
|
|
eab1cb4c79 | ||
|
|
5af0b35cd1 | ||
|
|
b2fb80905d | ||
|
|
b9ce89fe2d | ||
|
|
44e029acba | ||
|
|
cf02473d17 | ||
|
|
3667fd1634 | ||
|
|
3e6faf505e | ||
|
|
b304b248f1 | ||
|
|
f4d6099554 | ||
|
|
ba701f84ae | ||
|
|
c59fbc5ebf | ||
|
|
ddb1601ce1 | ||
|
|
d70319903c | ||
|
|
7ebbd2b13f | ||
|
|
5651f6d0b3 | ||
|
|
87623fb643 | ||
|
|
fdfe028bc0 | ||
|
|
c944c54547 | ||
|
|
1642baab76 | ||
|
|
44ebe1e119 | ||
|
|
319c7c4cfa | ||
|
|
8b85b51df2 | ||
|
|
4977dd62c2 | ||
|
|
c02dc35517 | ||
|
|
4de583af1e | ||
|
|
291bc52a97 | ||
|
|
82defd4fab | ||
|
|
3be98af9df | ||
|
|
7a7f9418ef | ||
|
|
bb4895da05 | ||
|
|
81d079d14f | ||
|
|
0c407e45d1 | ||
|
|
7556f16c24 | ||
|
|
24d29bf4b9 | ||
|
|
752b8abe5d | ||
|
|
10ad85e7eb | ||
|
|
675c2dbb0a | ||
|
|
ac7146e3ea | ||
|
|
24e1d70f27 | ||
|
|
f2a86a98ff | ||
|
|
53449e6941 | ||
|
|
bb95521e5e | ||
|
|
8f690b1c2e | ||
|
|
02e7536918 | ||
|
|
a8b32590d1 | ||
|
|
ab8d3f6ba0 | ||
|
|
dafdea73e8 | ||
|
|
c4a8c9addb | ||
|
|
9f7fd64618 | ||
|
|
9fd79d4154 | ||
|
|
8ae6f172e4 | ||
|
|
d9a33e8cfc | ||
|
|
48598817fd | ||
|
|
30d02fc85d | ||
|
|
8372c65f09 | ||
|
|
f9c0366b85 | ||
|
|
563db09ca2 | ||
|
|
ae655436ba | ||
|
|
9d9669a398 | ||
|
|
d7d4c239b8 | ||
|
|
dd9667a0bd | ||
|
|
c66c19dca7 | ||
|
|
514558ac42 | ||
|
|
622446d9c0 | ||
|
|
f739af6889 | ||
|
|
1bc0374d55 | ||
|
|
e4afc8ae10 | ||
|
|
a6153fb69a | ||
|
|
04df00a6b1 | ||
|
|
b9881e1cc0 | ||
|
|
7c191b0c95 | ||
|
|
b977a9d045 | ||
|
|
531ff4a6f1 | ||
|
|
2cf4942d9d | ||
|
|
c11483ddf2 | ||
|
|
11781253d5 | ||
|
|
c5f3c426b8 | ||
|
|
f9d0d41c89 | ||
|
|
ca91932a3f | ||
|
|
870651968b | ||
|
|
3ef8cc85b5 | ||
|
|
8c875be390 | ||
|
|
b960658234 | ||
|
|
0f85e7bf10 | ||
|
|
d0240161dd | ||
|
|
9d35b79f74 | ||
|
|
97e8e6ab4b | ||
|
|
a9c6e8c700 | ||
|
|
6699892497 | ||
|
|
ab15540c61 | ||
|
|
91135aac55 | ||
|
|
846ae00d1c | ||
|
|
16401e6b8b | ||
|
|
f3042d0ee9 | ||
|
|
1446fbbbb3 | ||
|
|
04ab537fff | ||
|
|
6d591067d3 | ||
|
|
d4e59ca6e5 | ||
|
|
c60a02b20a | ||
|
|
b429a4bad2 | ||
|
|
9136f32428 | ||
|
|
3aee8becf0 | ||
|
|
5e2a9a73c8 | ||
|
|
3f2db180f4 | ||
|
|
3386ea3aec | ||
|
|
e4246c209a | ||
|
|
4a450bf1de | ||
|
|
94339026c8 | ||
|
|
9985faa8e1 | ||
|
|
b3d84ba596 | ||
|
|
9480f035b1 | ||
|
|
83f6e98c4a | ||
|
|
4e69ce173f | ||
|
|
292083a89a | ||
|
|
68b828f57b | ||
|
|
baac18df51 | ||
|
|
10d4a710cc | ||
|
|
c1c1f5a027 | ||
|
|
803ed35311 | ||
|
|
ca46323d3b | ||
|
|
3b719e5582 | ||
|
|
820edb6e22 | ||
|
|
9c9c46b44b | ||
|
|
6e33b18025 | ||
|
|
03923943f5 | ||
|
|
8cabe31eb2 | ||
|
|
3384258382 | ||
|
|
4e3a04b75d | ||
|
|
766de82a37 | ||
|
|
6c6dafec1d | ||
|
|
7369782125 | ||
|
|
7961750052 | ||
|
|
13057efcd1 | ||
|
|
d0c3b071c2 | ||
|
|
e25e5b8aa9 | ||
|
|
9915995240 | ||
|
|
9a7d215464 | ||
|
|
851a90a06e | ||
|
|
7cd1d0197d | ||
|
|
6b9144c448 | ||
|
|
df42477f5f | ||
|
|
348d7e9bd1 | ||
|
|
e35085dbd5 | ||
|
|
4792c7bec8 | ||
|
|
d447241caa | ||
|
|
9102584338 | ||
|
|
96d19e1900 | ||
|
|
34eb6ee089 | ||
|
|
b30f332a18 | ||
|
|
353e5c962f | ||
|
|
8bb9859ca9 | ||
|
|
29a3dec05b | ||
|
|
ebe5db6ab0 | ||
|
|
e5b2e99636 | ||
|
|
06812fed5a | ||
|
|
527769e854 | ||
|
|
9e8bd6ca71 | ||
|
|
1a75dd0af4 | ||
|
|
3ab0747e5d | ||
|
|
3fac49ca7f | ||
|
|
ddb98ad89e | ||
|
|
bc3e345179 | ||
|
|
3d1c039280 | ||
|
|
92281682b0 | ||
|
|
b205551593 | ||
|
|
63ee0fc281 | ||
|
|
26c39578ba | ||
|
|
25dbe773b6 | ||
|
|
ce24c91b3c | ||
|
|
b308f1e14c | ||
|
|
f29160fee9 | ||
|
|
9a6a565aab | ||
|
|
d81fbcae21 | ||
|
|
5c0d4194c6 | ||
|
|
abde635eec | ||
|
|
45297a8c33 | ||
|
|
1848864b2a | ||
|
|
314a0cdfb5 | ||
|
|
dff48bd46d | ||
|
|
bd36be7355 | ||
|
|
006ec816de | ||
|
|
b9e4f65801 | ||
|
|
ce3883ab88 | ||
|
|
f6821d7e2b | ||
|
|
101c8e698c | ||
|
|
e508f6cd80 | ||
|
|
0ceaaf2f99 | ||
|
|
97936956e0 | ||
|
|
f43b7fd8cb | ||
|
|
661229165d | ||
|
|
6f7926deda | ||
|
|
2e22785e1d | ||
|
|
059897dcdf | ||
|
|
77f0e6fd6c | ||
|
|
c1e7f231a5 | ||
|
|
ba4b8dd0f8 | ||
|
|
2b4e4cb4d0 | ||
|
|
e6cfb6c78a | ||
|
|
ac477aac8b | ||
|
|
1cd715586c | ||
|
|
728fdec6a3 | ||
|
|
f4550ecac9 | ||
|
|
b2dbb43f8d | ||
|
|
5b39e2c362 | ||
|
|
263377359e | ||
|
|
4aa396b752 | ||
|
|
0db44356a8 | ||
|
|
f599466217 | ||
|
|
7323fb1472 | ||
|
|
1aa59cd008 | ||
|
|
64537b619e | ||
|
|
f031d4df6b | ||
|
|
b52e1e9c31 | ||
|
|
150a2622f4 | ||
|
|
fa856b2e85 | ||
|
|
0e0fe81a58 | ||
|
|
487851e9cc | ||
|
|
c3027b39c8 | ||
|
|
c965be11cd | ||
|
|
e644880554 | ||
|
|
7b7d9c98b2 | ||
|
|
21d4b7ad04 | ||
|
|
f4f12d05f6 | ||
|
|
8658ce09e4 | ||
|
|
8d49c9a301 | ||
|
|
91c9fd4f2f | ||
|
|
da070219a7 | ||
|
|
6b63759d33 | ||
|
|
402a4a9472 | ||
|
|
adf40640d2 | ||
|
|
15d373589f | ||
|
|
564249c2f2 | ||
|
|
2ebcb80a90 | ||
|
|
09553057af | ||
|
|
3bf774d41f | ||
|
|
a08f7e5108 | ||
|
|
07ad6b0230 | ||
|
|
cb7a2aa23b | ||
|
|
660c3f6a01 | ||
|
|
295b805899 | ||
|
|
088d0ef864 | ||
|
|
03267d7db0 | ||
|
|
f993923e1d | ||
|
|
2abb9fbf24 | ||
|
|
3757186ff4 | ||
|
|
391ba33b17 | ||
|
|
295da28380 | ||
|
|
1bb50db9c4 | ||
|
|
06cff6fa39 | ||
|
|
be6f0ae72d | ||
|
|
fb169161c2 | ||
|
|
db949f4220 | ||
|
|
cd146f66bc | ||
|
|
f92932d8a3 | ||
|
|
8651a23f27 | ||
|
|
e8543aae3b | ||
|
|
7c0295b115 | ||
|
|
a2b1fd5222 | ||
|
|
0274e05301 | ||
|
|
f399c55e45 | ||
|
|
d16ad4d8d5 | ||
|
|
2de462769c | ||
|
|
c94975a985 | ||
|
|
cf6dc5f641 | ||
|
|
ea1b1f7937 | ||
|
|
4710d69137 | ||
|
|
3acbe8a5fe | ||
|
|
c757173b5a | ||
|
|
fe20afc46c | ||
|
|
38e5246c12 | ||
|
|
a978eb3611 | ||
|
|
9401dbaa30 | ||
|
|
11d8cadb8e | ||
|
|
1cce624f9f | ||
|
|
bbfc92aae5 | ||
|
|
43fb87bd47 | ||
|
|
5b2d92ca21 | ||
|
|
fd5c7a7844 | ||
|
|
48426f9f5a | ||
|
|
d584977a10 | ||
|
|
b1010f561c | ||
|
|
7fe8da9143 | ||
|
|
6bab223970 | ||
|
|
b5f4a45154 | ||
|
|
5529527847 | ||
|
|
e077c00d65 | ||
|
|
bb3db6d697 | ||
|
|
43eec5dbbd | ||
|
|
13c7d7fd8d | ||
|
|
d21bf0a50f | ||
|
|
a69429b7ab | ||
|
|
d0a7cf9430 | ||
|
|
b179265c36 | ||
|
|
d1d7099d3e | ||
|
|
cca47f009b | ||
|
|
71687c6687 | ||
|
|
332e9a0438 | ||
|
|
8a563f8be2 | ||
|
|
f8c3b59ba5 | ||
|
|
2358935727 | ||
|
|
73eee7cc30 | ||
|
|
82adc69468 | ||
|
|
a6de44b5f5 | ||
|
|
c266936beb | ||
|
|
5986063b6a | ||
|
|
5bf40d88b8 | ||
|
|
bd90713a68 | ||
|
|
1389e9d7a0 | ||
|
|
e39e51d8a6 | ||
|
|
6ae140c912 | ||
|
|
f5dbc1490f | ||
|
|
835393d4e8 | ||
|
|
2ad651089e | ||
|
|
4ee7a32a00 | ||
|
|
816fa79f7d | ||
|
|
62eb3926de | ||
|
|
6f4bc433f6 | ||
|
|
e2442777f9 | ||
|
|
edd56c05fc | ||
|
|
3551bc3f72 | ||
|
|
abef0172d7 | ||
|
|
0ec1827548 | ||
|
|
aff2556f9b | ||
|
|
e94e0e2ba0 | ||
|
|
eb7128423a | ||
|
|
119dae42e3 | ||
|
|
71a419e54d | ||
|
|
4c9cda0cb3 | ||
|
|
aeb48c365a | ||
|
|
d78cafdd37 | ||
|
|
1ceb88c2c9 | ||
|
|
e40555df96 | ||
|
|
e731d32021 | ||
|
|
41e74998e1 | ||
|
|
966d814912 | ||
|
|
585c17d35e | ||
|
|
7b4320ba25 | ||
|
|
802231990c | ||
|
|
f301b46d60 | ||
|
|
dc99ff49fd | ||
|
|
514515179b | ||
|
|
c17dd09d06 | ||
|
|
5fafcd7b6f | ||
|
|
27b3d81b9b | ||
|
|
a903f5df5e | ||
|
|
d3ed5912bd | ||
|
|
da0838e0af | ||
|
|
e9b056ec8b | ||
|
|
4890f3786e | ||
|
|
76506813a6 | ||
|
|
36073bca2d | ||
|
|
db6b40130e | ||
|
|
cf3fd7d82f | ||
|
|
91f51b9179 | ||
|
|
e980f93c62 | ||
|
|
86d0ad731c | ||
|
|
4503ebfedd | ||
|
|
5831fb8b1c | ||
|
|
5647814e63 | ||
|
|
ec2ed7fa1a | ||
|
|
deb95f9f01 | ||
|
|
d051c2af48 | ||
|
|
716ee9d600 | ||
|
|
c8b458e0b6 | ||
|
|
2ccee151e2 | ||
|
|
671eb82136 | ||
|
|
0e191e8cf7 | ||
|
|
1db8fc903e | ||
|
|
6eccd5edb0 | ||
|
|
171e13ae33 | ||
|
|
c62fa97050 | ||
|
|
8b76d602d7 | ||
|
|
d5a1ad4b12 | ||
|
|
66a5a7c432 | ||
|
|
479cba632c | ||
|
|
c8baaa79a2 | ||
|
|
724b24c19d | ||
|
|
bbfdac8e6c | ||
|
|
734626eece | ||
|
|
f8eadc740b | ||
|
|
27d883695d | ||
|
|
ec079866b3 | ||
|
|
b7dd5ac37b | ||
|
|
1bcfe0b5f8 | ||
|
|
3577faa758 | ||
|
|
5dd0351bdc | ||
|
|
174e2d0049 | ||
|
|
1c30a30359 | ||
|
|
c2879109bf | ||
|
|
655a7f70c8 | ||
|
|
d5b405b847 | ||
|
|
124477a75f | ||
|
|
6dc22ce935 | ||
|
|
2a1c424c55 | ||
|
|
fe39a692ff | ||
|
|
b3fe464587 | ||
|
|
01693f870b | ||
|
|
85f4591312 | ||
|
|
9caf7a5572 | ||
|
|
6570b359d3 | ||
|
|
bd3c07f6d8 | ||
|
|
20aef2616b | ||
|
|
8e7ee34266 | ||
|
|
9c59b10527 | ||
|
|
000c3ee8bd | ||
|
|
66f18b9b14 | ||
|
|
7645309311 | ||
|
|
ef622e2079 | ||
|
|
c6c2004e6c | ||
|
|
435e1b48b9 | ||
|
|
678bceccd9 | ||
|
|
ce7a63349d | ||
|
|
6f0ed456c9 | ||
|
|
0494495eec | ||
|
|
881f15cf24 | ||
|
|
df82599909 | ||
|
|
376dbe1ab3 | ||
|
|
2fb1e9b1ef | ||
|
|
942da3bc43 | ||
|
|
9885f47c0f | ||
|
|
19bae8c29d | ||
|
|
62fc1bb79a | ||
|
|
6f75d07613 | ||
|
|
4e323adfb6 | ||
|
|
04da3314d2 | ||
|
|
45c2724a75 | ||
|
|
355a9ae2b7 | ||
|
|
8355cba6ff | ||
|
|
a91b75c3de | ||
|
|
171eedd8b5 | ||
|
|
f539152039 | ||
|
|
532c302335 | ||
|
|
52745c58c0 | ||
|
|
0c8befaef0 | ||
|
|
87c0c40234 | ||
|
|
2503c017ab | ||
|
|
180314deff | ||
|
|
4e07f48f8a | ||
|
|
e3f649ffc4 | ||
|
|
3a6bae411b | ||
|
|
c1fef3fa07 | ||
|
|
8609df051f | ||
|
|
725b525956 | ||
|
|
af928c00f8 | ||
|
|
438f9549c1 | ||
|
|
2fd4e7e57d | ||
|
|
ce9c480a07 | ||
|
|
ea8d799eae | ||
|
|
6af9cd65a6 | ||
|
|
2f1b4241ea | ||
|
|
a1bf0516e5 | ||
|
|
bcb02f95ac | ||
|
|
eec248e5fb | ||
|
|
2dd8933e92 | ||
|
|
59ea28dbff | ||
|
|
7ec2229ab5 | ||
|
|
a12daf3827 | ||
|
|
0e3a9615c3 | ||
|
|
f13d3fab5d | ||
|
|
fe1fb5acf7 | ||
|
|
cddc1cd258 | ||
|
|
fc4cf67f1d | ||
|
|
5556d3409b | ||
|
|
e2361151b9 | ||
|
|
ff0361d73e | ||
|
|
e13f39df86 | ||
|
|
83aedb8e6a | ||
|
|
e0bc07c602 | ||
|
|
48439f4500 | ||
|
|
b1ee965bda | ||
|
|
caf3c13dc9 | ||
|
|
082ec42981 | ||
|
|
133b4e8582 | ||
|
|
036515a2e1 | ||
|
|
266dd32b16 | ||
|
|
adbe5673f5 | ||
|
|
5c215930b2 | ||
|
|
227f5b65f6 | ||
|
|
6c7d0e0bdd | ||
|
|
a08851e959 | ||
|
|
0a7d809228 | ||
|
|
18a5508790 | ||
|
|
06a53d5845 | ||
|
|
95b15612f9 | ||
|
|
e96df5d82f | ||
|
|
ee0f01ca38 | ||
|
|
07a5d15086 | ||
|
|
e1ee0b2407 | ||
|
|
4d4e82fc63 | ||
|
|
88ae28d5ea | ||
|
|
24eecba5c8 |
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -29,3 +29,4 @@
|
|||
user-emacs.*
|
||||
prot-emacs-pre-custom.*
|
||||
prot-emacs-post-custom.*
|
||||
*-autoloads.el
|
||||
|
|
|
|||
4
COPYING
4
COPYING
|
|
@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
|
|||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
Dotfile collection with post-install customisations for GNU/Linux
|
||||
Copyright (C) 2019 Protesilaos Stavrou
|
||||
Copyright (C) 2019-2026 Protesilaos
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
|||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
Protesilaos Stavrou's Dotfiles Copyright (C) 2019 Protesilaos Stavrou
|
||||
Protesilaos's Dotfiles Copyright (C) 2019-2026 Protesilaos
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[user]
|
||||
name = Protesilaos Stavrou
|
||||
name = Protesilaos
|
||||
email = info@protesilaos.com
|
||||
signingkey = C3C6BC9BBE2F62C7C586EBAA99BD6459CD5CA3EA
|
||||
[core]
|
||||
|
|
|
|||
63
README.md
63
README.md
|
|
@ -1,4 +1,4 @@
|
|||
# My Emacs and other configurations for Arch Linux
|
||||
# My Emacs and other configurations for Linux
|
||||
|
||||
This is the set of files that powers my day-to-day computing experience.
|
||||
GNU Emacs is its centrepiece, with other programs providing ancillary
|
||||
|
|
@ -26,12 +26,10 @@ or contribute any fixes, if you will.
|
|||
|
||||
## Emacs setup
|
||||
|
||||
I do not recommend you reproduce my Emacs setup because I do not use
|
||||
the de facto standard of `use-package` to configure packages. I
|
||||
prefer a simpler approach.
|
||||
|
||||
If you insist though, the files are in the `emacs` directory. Add them
|
||||
to your home directory with:
|
||||
I do not recommend you reproduce my Emacs setup because I do not
|
||||
design it as a distribution for other people. If you insist though,
|
||||
the files are in the `emacs` directory. Add them to your home
|
||||
directory with:
|
||||
|
||||
```sh
|
||||
/path/to/prot-dotfiles $ stow -t "$HOME" emacs
|
||||
|
|
@ -58,11 +56,11 @@ run stow again with the `-R` flag:
|
|||
|
||||
## Window managers
|
||||
|
||||
My tiling window managers are bspwm, herbstluftwm, and i3 (in no
|
||||
particular order). They are all configured to be almost the same:
|
||||
My tiling window managers for Xorg are bspwm, herbstluftwm, and i3 (in
|
||||
no particular order). They are all configured to be almost the same:
|
||||
they share settings for the wallpaper, system panel, theme, keyboard
|
||||
layout, and display compositor. All settings are in the `xorg-twm`
|
||||
directory ("twm" stands for "tiling window manager"). What
|
||||
layout, and display compositor. All settings are in the `xorg-twm`
|
||||
directory ("twm" stands for "tiling window manager"). What
|
||||
differentiates the window managers is their individual features.
|
||||
|
||||
+ **bspwm:** I have been using it for years and consider it top-notch.
|
||||
|
|
@ -72,25 +70,26 @@ differentiates the window managers is their individual features.
|
|||
them.
|
||||
|
||||
+ **herbstluftwm (hlwm):** herbstluftwm prioritises manual tiling
|
||||
methods and can treat arbitrary rectangles of a monitor as virtual
|
||||
monitors. The virtual monitors feature is perfect for anyone with a
|
||||
widescreen display. The one I have (which is not mine, but anyway)
|
||||
is 2560x1080, so I split it by default into a regular 1920x1080 area
|
||||
and another "sidebar" of 640x1080. herbstluftwm has the concept of
|
||||
"frame", which is a container of regular windows. Each frame can
|
||||
have its own layout, including a vertical/horizontal stack, grid,
|
||||
and tabbed.
|
||||
methods. It has the concept of "frames", which are rectangles that
|
||||
include regular app windows. Each frame has its own dimensions are
|
||||
layout. For example, it is possible to have a frame where windows
|
||||
are vertically layed out one below the other and another frame with
|
||||
a tabbed layout. What makes this even more appealing, is the ease
|
||||
with which a workspace can be preconfigured (or dymanically
|
||||
adjusted) to a given arrangement of frames and their layouts. I
|
||||
think herbstluftwm is optimal for screens wider than the the regular
|
||||
1920x1080. Otherwise, the manual tiling method adds cognitive load
|
||||
that the user does not have while using bspwm.
|
||||
|
||||
+ **i3 (or i3wm):** The first tiling window manager I ever used (circa
|
||||
2017). The reason I abandoned it back in the day in favour of bspwm
|
||||
2017). The reason I abandoned it back in the day in favour of bspwm
|
||||
is because its default tiling method requires manual intervention to
|
||||
change the split direction. On a small laptop monitor, I prefer
|
||||
this to be done automatically, hence bspwm. Though on a wide
|
||||
monitor, I typically keep the split direction constant. i3 has the
|
||||
concept of the "container" (same as herbstluftwm "frame"), which can
|
||||
be set to stacked, tabbed, or tiled layouts. As such, i3 is
|
||||
somewhere between bspwm and herbstluftwm. Choose according to your
|
||||
needs.
|
||||
change the split direction. On a small laptop monitor, I prefer this
|
||||
to be done automatically, hence bspwm. Though on a wide monitor, I
|
||||
typically keep the split direction constant. i3 has the concept of
|
||||
the "container" (same as the herbstluftwm "frame"), which can be set
|
||||
to stacked, tabbed, or tiled layouts. As such, i3 is somewhere
|
||||
between bspwm and herbstluftwm. Choose according to your needs.
|
||||
|
||||
Check the `xtwm-key-binding-cheatsheet.md` file for an overview of
|
||||
their key bindings.
|
||||
|
|
@ -100,9 +99,13 @@ always use herbstluftwm because of its ability to support virtual
|
|||
monitors. Otherwise I oscillate between bspwm and i3, depending on
|
||||
the machine I am using.
|
||||
|
||||
What about Wayland? It is not ready yet: I am missing something like
|
||||
sxhkd and certain applications do not work properly on it. I may
|
||||
check again in a few years.
|
||||
What about Wayland? I have experimented with it on GNOME where it
|
||||
works fine. I even have some configurations for tiling compositors,
|
||||
but am not happy with those. The Wayland ecosystem is not ready yet
|
||||
for my use-case: I am missing something like sxhkd and certain
|
||||
applications do not work properly on it, such screen sharing. I will
|
||||
check again some time over the medium term, as Wayland seems
|
||||
inevitable.
|
||||
|
||||
## Copying
|
||||
|
||||
|
|
|
|||
|
|
@ -26,24 +26,31 @@
|
|||
# constant width of 300
|
||||
width = 300
|
||||
|
||||
# The maximum height of a single notification, excluding the frame.
|
||||
height = 300
|
||||
# The height of a single notification, excluding the frame.
|
||||
# dynamic height from 0 to 300
|
||||
height = (0, 300)
|
||||
# constant height of 300
|
||||
# height = 300
|
||||
# NOTE: Dunst from version 1.11 and older don't support dynamic height
|
||||
# and the given value is treated as the maximum height
|
||||
|
||||
# Position the notification in the top right corner
|
||||
# Position the notification in the bottom right corner
|
||||
origin = bottom-right
|
||||
|
||||
# Offset from the origin
|
||||
offset = 10x50
|
||||
# NOTE: Dunst from version 1.11 and older use this alternative notation
|
||||
# offset = 10x50
|
||||
offset = (10, 25)
|
||||
|
||||
# Scale factor. It is auto-detected if value is 0.
|
||||
scale = 0
|
||||
|
||||
# Maximum number of notification (0 means no limit)
|
||||
notification_limit = 0
|
||||
notification_limit = 20
|
||||
|
||||
### Progress bar ###
|
||||
|
||||
# Turn on the progess bar. It appears when a progress hint is passed with
|
||||
# Turn on the progress bar. It appears when a progress hint is passed with
|
||||
# for example dunstify -h int:value:12
|
||||
progress_bar = true
|
||||
|
||||
|
|
@ -60,6 +67,19 @@
|
|||
# Set the maximum width for the progress bar
|
||||
progress_bar_max_width = 300
|
||||
|
||||
# Corner radius for the progress bar. 0 disables rounded corners.
|
||||
progress_bar_corner_radius = 0
|
||||
|
||||
# Define which corners to round when drawing the progress bar. If progress_bar_corner_radius
|
||||
# is set to 0 this option will be ignored.
|
||||
progress_bar_corners = all
|
||||
|
||||
# Corner radius for the icon image.
|
||||
icon_corner_radius = 0
|
||||
|
||||
# Define which corners to round when drawing the icon image. If icon_corner_radius
|
||||
# is set to 0 this option will be ignored.
|
||||
icon_corners = all
|
||||
|
||||
# Show how many messages are currently hidden (because of
|
||||
# notification_limit).
|
||||
|
|
@ -73,7 +93,8 @@
|
|||
# Draw a line of "separator_height" pixel height between two
|
||||
# notifications.
|
||||
# Set to 0 to disable.
|
||||
separator_height = 1
|
||||
# If gap_size is greater than 0, this setting will be ignored.
|
||||
separator_height = 2
|
||||
|
||||
# Padding between text and separator.
|
||||
padding = 8
|
||||
|
|
@ -91,6 +112,12 @@
|
|||
# Defines color of the frame around the notification window.
|
||||
frame_color = "#aaaaaa"
|
||||
|
||||
# Size of gap to display between notifications - requires a compositor.
|
||||
# If value is greater than 0, separator_height will be ignored and a border
|
||||
# of size frame_width will be drawn around each notification instead.
|
||||
# Click events on gaps do not currently propagate to applications below.
|
||||
gap_size = 0
|
||||
|
||||
# Define a color for the separator.
|
||||
# possible values are:
|
||||
# * auto: dunst tries to find a color fitting to the background;
|
||||
|
|
@ -99,7 +126,12 @@
|
|||
# * anything else will be interpreted as a X color.
|
||||
separator_color = frame
|
||||
|
||||
# Sort messages by urgency.
|
||||
# Sort type.
|
||||
# possible values are:
|
||||
# * id: sort by id
|
||||
# * urgency_ascending: sort by urgency (low then normal then critical)
|
||||
# * urgency_descending: sort by urgency (critical then normal then low)
|
||||
# * update: sort by update (most recent always at the top)
|
||||
sort = yes
|
||||
|
||||
# Don't remove messages, if the user is idle (no mouse or keyboard input)
|
||||
|
|
@ -107,11 +139,11 @@
|
|||
# Set to 0 to disable.
|
||||
# A client can set the 'transient' hint to bypass this. See the rules
|
||||
# section for how to disable this if necessary
|
||||
idle_threshold = 120
|
||||
# idle_threshold = 120
|
||||
|
||||
### Text ###
|
||||
|
||||
font = Monospace 9.5
|
||||
font = Sans 8
|
||||
|
||||
# The spacing between lines. If the height is smaller than the
|
||||
# font height, it will get raised to the font height.
|
||||
|
|
@ -125,7 +157,7 @@
|
|||
# <u>underline</u>
|
||||
#
|
||||
# For a complete reference see
|
||||
# <https://developer.gnome.org/pango/stable/pango-Markup.html>.
|
||||
# <https://docs.gtk.org/Pango/pango_markup.html>.
|
||||
#
|
||||
# strip: This setting is provided for compatibility with some broken
|
||||
# clients that send markup even though it's not enabled on the
|
||||
|
|
@ -184,19 +216,28 @@
|
|||
|
||||
### Icons ###
|
||||
|
||||
# Align icons left/right/off
|
||||
# Recursive icon lookup. You can set a single theme, instead of having to
|
||||
# define all lookup paths.
|
||||
enable_recursive_icon_lookup = true
|
||||
|
||||
# Set icon theme (only used for recursive icon lookup)
|
||||
# icon_theme = Adwaita
|
||||
# You can also set multiple icon themes, with the leftmost one being used first.
|
||||
icon_theme = "Adwaita, Papirus-Dark, Papirus-Light"
|
||||
|
||||
# Align icons left/right/top/off
|
||||
icon_position = left
|
||||
|
||||
# Scale small icons up to this size, set to 0 to disable. Helpful
|
||||
# for e.g. small files or high-dpi screens. In case of conflict,
|
||||
# max_icon_size takes precedence over this.
|
||||
min_icon_size = 0
|
||||
min_icon_size = 32
|
||||
|
||||
# Scale larger icons down to this size, set to 0 to disable
|
||||
max_icon_size = 32
|
||||
max_icon_size = 128
|
||||
|
||||
# Paths to default icons.
|
||||
icon_path = /usr/share/icons/Adwaita/16x16/status/:/usr/share/icons/Adwaita/16x16/devices/
|
||||
# Paths to default icons (only necessary when not using recursive icon lookup)
|
||||
icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
|
||||
|
||||
### History ###
|
||||
|
||||
|
|
@ -218,10 +259,10 @@
|
|||
# Always run rule-defined scripts, even if the notification is suppressed
|
||||
always_run_script = true
|
||||
|
||||
# Define the title of the windows spawned by dunst
|
||||
# Define the title of the windows spawned by dunst (X11 only)
|
||||
title = Dunst
|
||||
|
||||
# Define the class of the windows spawned by dunst
|
||||
# Define the class of the windows spawned by dunst (X11 only)
|
||||
class = Dunst
|
||||
|
||||
# Define the corner radius of the notification window
|
||||
|
|
@ -231,6 +272,13 @@
|
|||
# notification height to avoid clipping text and/or icons.
|
||||
corner_radius = 0
|
||||
|
||||
# Define which corners to round when drawing the window. If the corner radius
|
||||
# is set to 0 this option will be ignored.
|
||||
#
|
||||
# Comma-separated list of the corners. The accepted corner values are bottom-right,
|
||||
# bottom-left, top-right, top-left, top, bottom, left, right or all.
|
||||
corners = all
|
||||
|
||||
# Ignore the dbus closeNotification message.
|
||||
# Useful to enforce the timeout set by dunst configuration. Without this
|
||||
# parameter, an application may close the notification sent before the
|
||||
|
|
@ -240,7 +288,7 @@
|
|||
### Wayland ###
|
||||
# These settings are Wayland-specific. They have no effect when using X11
|
||||
|
||||
# Uncomment this if you want to let notications appear under fullscreen
|
||||
# Uncomment this if you want to let notifications appear under fullscreen
|
||||
# applications (default: overlay)
|
||||
# layer = top
|
||||
|
||||
|
|
@ -295,23 +343,25 @@
|
|||
background = "#222222"
|
||||
foreground = "#888888"
|
||||
timeout = 4
|
||||
# Icon for notifications with low urgency, uncomment to enable
|
||||
#icon = /path/to/icon
|
||||
# Icon for notifications with low urgency
|
||||
default_icon = dialog-information
|
||||
|
||||
[urgency_normal]
|
||||
background = "#333333"
|
||||
foreground = "#ffffff"
|
||||
timeout = 6
|
||||
# Icon for notifications with normal urgency, uncomment to enable
|
||||
#icon = /path/to/icon
|
||||
override_pause_level = 30
|
||||
# Icon for notifications with normal urgency
|
||||
default_icon = dialog-information
|
||||
|
||||
[urgency_critical]
|
||||
background = "#900000"
|
||||
foreground = "#ffffff"
|
||||
frame_color = "#ff0000"
|
||||
timeout = 0
|
||||
# Icon for notifications with critical urgency, uncomment to enable
|
||||
#icon = /path/to/icon
|
||||
override_pause_level = 60
|
||||
# Icon for notifications with critical urgency
|
||||
default_icon = dialog-warning
|
||||
|
||||
# Every section that isn't one of the above is interpreted as a rules to
|
||||
# override settings for certain messages.
|
||||
|
|
@ -336,9 +386,18 @@
|
|||
# new_icon
|
||||
# set_stack_tag
|
||||
# set_transient
|
||||
# set_category
|
||||
# timeout
|
||||
# urgency
|
||||
# icon_position
|
||||
# skip_display
|
||||
# history_ignore
|
||||
# action_name
|
||||
# word_wrap
|
||||
# ellipsize
|
||||
# alignment
|
||||
# hide_text
|
||||
# override_pause_level
|
||||
#
|
||||
# Shell-like globbing will get expanded.
|
||||
#
|
||||
|
|
@ -346,6 +405,10 @@
|
|||
# GLib based applications export their desktop-entry name. In comparison to the appname,
|
||||
# the desktop-entry won't get localized.
|
||||
#
|
||||
# You can also allow a notification to appear even when paused. Notification will appear whenever notification's override_pause_level >= dunst's paused level.
|
||||
# This can be used to set partial pause modes, where more urgent notifications get through, but less urgent stay paused. To do that, you can override the following in the rules:
|
||||
# override_pause_level = X
|
||||
|
||||
# SCRIPTING
|
||||
# You can specify a script that gets run when the rule matches by
|
||||
# setting the "script" option.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# dunst_toggle_notify --- Notify on Dunst pause/activation
|
||||
#
|
||||
# Copyright (c) 2021-2023 Protesilaos Stavrou <info@protesilaos.com>
|
||||
# Copyright (C) 2021-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -1,31 +1,5 @@
|
|||
(defvar prot-emacs-tiling-window-manager-regexp "bspwm\\|herbstluftwm\\|i3"
|
||||
"Regular expression to tiling window managers.
|
||||
See definition of `prot-emacs-with-desktop-session'.")
|
||||
|
||||
(defmacro prot-emacs-with-desktop-session (&rest body)
|
||||
"Expand BODY if desktop session is not a tiling window manager.
|
||||
See `prot-emacs-tiling-window-manager-regexp' for what
|
||||
constitutes a matching tiling window manager."
|
||||
(declare (indent 0))
|
||||
`(when-let ((session (getenv "DESKTOP_SESSION"))
|
||||
((not (string-match-p session prot-emacs-tiling-window-manager-regexp))))
|
||||
,@body))
|
||||
|
||||
(defun prot-emacs-add-to-list (list element)
|
||||
"Add to symbol of LIST the given ELEMENT.
|
||||
Simplified version of `add-to-list'."
|
||||
(set list (cons element (symbol-value list))))
|
||||
|
||||
(prot-emacs-with-desktop-session
|
||||
(mapc
|
||||
(lambda (var)
|
||||
(prot-emacs-add-to-list var '(width . (text-pixels . 1200)))
|
||||
(prot-emacs-add-to-list var '(height . (text-pixels . 900)))
|
||||
(prot-emacs-add-to-list var '(scroll-bar-width . 10)))
|
||||
'(default-frame-alist initial-frame-alist)))
|
||||
|
||||
(setq frame-resize-pixelwise t
|
||||
frame-inhibit-implied-resize t
|
||||
frame-inhibit-implied-resize 'force
|
||||
frame-title-format '("%b")
|
||||
ring-bell-function 'ignore
|
||||
use-dialog-box t ; only for mouse events, which I seldom use
|
||||
|
|
@ -37,13 +11,60 @@ Simplified version of `add-to-list'."
|
|||
inhibit-startup-echo-area-message user-login-name ; read the docstring
|
||||
inhibit-startup-buffer-menu t)
|
||||
|
||||
;; I do not use those graphical elements by default, but I do enable
|
||||
;; them from time-to-time for testing purposes or to demonstrate
|
||||
;; something. NEVER tell a beginner to disable any of these. They
|
||||
;; are helpful.
|
||||
(menu-bar-mode -1)
|
||||
(scroll-bar-mode -1)
|
||||
(tool-bar-mode -1)
|
||||
(scroll-bar-mode -1)
|
||||
|
||||
(defvar prot-laptop-p (null (directory-empty-p "/sys/class/power_supply/"))
|
||||
"When non-nil, we assume to be working on a laptop.")
|
||||
|
||||
(when prot-laptop-p
|
||||
(add-hook 'window-size-change-functions #'frame-hide-title-bar-when-maximized))
|
||||
|
||||
(defvar prot-pgtk-p (string-match-p "PGTK" system-configuration-features)
|
||||
"When non-nil, this is a build --with-pgtk.
|
||||
PGTK is the Wayland-specific build of Emacs.")
|
||||
|
||||
(setq initial-frame-alist `((horizontal-scroll-bars . nil)
|
||||
(menu-bar-lines . 0)
|
||||
(tool-bar-lines . 0)
|
||||
(vertical-scroll-bars . nil)
|
||||
(scroll-bar-width . ,(if prot-pgtk-p 12 6))
|
||||
(width . (text-pixels . 800))
|
||||
(height . (text-pixels . 900))
|
||||
,@(unless prot-pgtk-p
|
||||
(list '(undecorated . t)))
|
||||
(border-width . 0)
|
||||
,@(when prot-laptop-p
|
||||
(list '(fullscreen . maximized)))))
|
||||
|
||||
;; Do it again after init so that any intermediate changes are not
|
||||
;; retained. Note that we cannot rely on setting this to
|
||||
;; `initial-frame-alist' as that may change in the meantime. We
|
||||
;; explicitly set the value to be certain of the outcome. This does
|
||||
;; not inhibit other programs from modifying the list, though I would
|
||||
;; consider it undesirable if they were touching these specific
|
||||
;; settings.
|
||||
(add-hook 'after-init-hook (lambda ()
|
||||
(setq default-frame-alist `((horizontal-scroll-bars . nil)
|
||||
(menu-bar-lines . 0)
|
||||
(tool-bar-lines . 0)
|
||||
(vertical-scroll-bars . nil)
|
||||
(scroll-bar-width . (if prot-pgtk-p 12 6))
|
||||
(width . (text-pixels . 800))
|
||||
(height . (text-pixels . 900))
|
||||
,@(unless prot-pgtk-p
|
||||
(list '(undecorated . t)))
|
||||
(border-width . 0)
|
||||
,@(when prot-laptop-p
|
||||
(list '(fullscreen . maximized)))))))
|
||||
|
||||
(defun prot-emacs-no-minibuffer-scroll-bar (frame)
|
||||
"Remove the minibuffer scroll bars from FRAME."
|
||||
(when scroll-bar-mode
|
||||
(set-window-scroll-bars (minibuffer-window frame) nil nil nil nil :persistent)))
|
||||
|
||||
(add-hook 'after-make-frame-functions #'prot-emacs-no-minibuffer-scroll-bar)
|
||||
|
||||
;; Temporarily increase the garbage collection threshold. These
|
||||
;; changes help shave off about half a second of startup time. The
|
||||
|
|
@ -64,7 +85,7 @@ Simplified version of `add-to-list'."
|
|||
|
||||
(add-hook 'emacs-startup-hook
|
||||
(lambda ()
|
||||
(setq gc-cons-threshold (* 1000 1000 8)
|
||||
(setq gc-cons-threshold (* 100 100 8)
|
||||
gc-cons-percentage 0.1
|
||||
file-name-handler-alist prot-emacs--file-name-handler-alist
|
||||
vc-handled-backends prot-emacs--vc-handled-backends)))
|
||||
|
|
@ -76,56 +97,6 @@ Simplified version of `add-to-list'."
|
|||
;; packages to work with, requiring a `package-refresh-contents'.
|
||||
(setq package-enable-at-startup t)
|
||||
|
||||
;;;; General theme code
|
||||
|
||||
(defun prot-emacs-theme-gsettings-dark-p ()
|
||||
"Return non-nil if gsettings (GNOME) has a dark theme.
|
||||
Return nil if the DESKTOP_SESSION is either bspwm or
|
||||
herbstluftwm, per the configuration of my dotfiles. Also check
|
||||
the `delight' shell script."
|
||||
(prot-emacs-with-desktop-session
|
||||
(string-match-p
|
||||
"dark"
|
||||
(shell-command-to-string "gsettings get org.gnome.desktop.interface color-scheme"))))
|
||||
|
||||
(defun prot-emacs-theme-twm-dark-p ()
|
||||
"Return non-nil if my custom setup has a dark theme.
|
||||
I place a file in ~/.config/prot-xtwm-active-theme which contains
|
||||
a single word describing my system-wide theme. This is part of
|
||||
my dotfiles. Check my `delight' shell script for more."
|
||||
(when-let ((file "~/.config/prot-xtwm-active-theme")
|
||||
((file-exists-p file)))
|
||||
(string-match-p
|
||||
"dark"
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(buffer-string)))))
|
||||
|
||||
(defun prot-emacs-theme-environment-dark-p ()
|
||||
"Return non-nil if environment theme is dark."
|
||||
(or (prot-emacs-theme-twm-dark-p)
|
||||
(prot-emacs-theme-gsettings-dark-p)))
|
||||
|
||||
(defun prot-emacs-re-enable-frame-theme (_frame)
|
||||
"Re-enable active theme, if any, upon FRAME creation.
|
||||
Add this to `after-make-frame-functions' so that new frames do
|
||||
not retain the generic background set by the function
|
||||
`prot-emacs-avoid-initial-flash-of-light'."
|
||||
(when-let ((theme (car custom-enabled-themes)))
|
||||
(enable-theme theme)))
|
||||
|
||||
;; NOTE 2023-02-05: The reason the following works is because (i) the
|
||||
;; `mode-line-format' is specified again and (ii) the
|
||||
;; `prot-emacs-theme-gsettings-dark-p' will load a dark theme.
|
||||
(defun prot-emacs-avoid-initial-flash-of-light ()
|
||||
"Avoid flash of light when starting Emacs, if needed.
|
||||
New frames are instructed to call `prot-emacs-re-enable-frame-theme'."
|
||||
(when (prot-emacs-theme-environment-dark-p)
|
||||
(setq mode-line-format nil)
|
||||
(set-face-attribute 'default nil :background "#000000" :foreground "#ffffff")
|
||||
(set-face-attribute 'mode-line nil :background "#000000" :foreground "#ffffff" :box 'unspecified)
|
||||
(add-hook 'after-make-frame-functions #'prot-emacs-re-enable-frame-theme)))
|
||||
|
||||
(prot-emacs-avoid-initial-flash-of-light)
|
||||
(setq user-lisp-directory (locate-user-emacs-file "prot-lisp/"))
|
||||
|
||||
(add-hook 'after-init-hook (lambda () (set-frame-name "home")))
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ directory as the init.el."
|
|||
|
||||
(defcustom prot-emacs-load-theme-family 'modus
|
||||
"Set of themes to load.
|
||||
Valid values are the symbols `ef', `modus', and `standard', which
|
||||
reference the `ef-themes', `modus-themes', and `standard-themes',
|
||||
respectively.
|
||||
Valid values are the symbols `doric', `ef', `modus', and `standard',
|
||||
which reference the `doric-themes', `ef-themes', `modus-themes', and
|
||||
`standard-themes', respectively.
|
||||
|
||||
A nil value does not load any of the above (use Emacs without a
|
||||
theme).
|
||||
|
|
@ -22,6 +22,7 @@ file. If that file exists in the Emacs directory, it is loaded
|
|||
before all other modules of my setup."
|
||||
:group 'prot-emacs
|
||||
:type '(choice :tag "Set of themes to load" :value modus
|
||||
(const :tag "The `doric-themes' module" doric)
|
||||
(const :tag "The `ef-themes' module" ef)
|
||||
(const :tag "The `modus-themes' module" modus)
|
||||
(const :tag "The `standard-themes' module" standard)
|
||||
|
|
@ -43,45 +44,35 @@ before all other modules of my setup."
|
|||
|
||||
(defcustom prot-emacs-completion-extras t
|
||||
"When non-nil load extras for minibuffer completion.
|
||||
These include packages such as `consult' and `embark'."
|
||||
These include the packages `marginalia', `consult', `corfu',
|
||||
`orderless', and `embark'."
|
||||
:group 'prot-emacs
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom prot-emacs-treesitter-extras t
|
||||
"When non-nil load extras for tree-sitter integration
|
||||
These include packages such as `expreg' and generally anything
|
||||
that adds functionality on top of what the major mode provides."
|
||||
:group 'prot-emacs
|
||||
:type 'boolean)
|
||||
(defcustom prot-emacs-completion-in-buffer 'corfu
|
||||
"Front-end for text completion in the buffer.
|
||||
When the value is either `corfu' or `company', use the package of the
|
||||
given name. When the value is `completion-preview' use the built-in
|
||||
`completion-preview-mode'. When the value is `corfu-completion-preview',
|
||||
then use the `corfu' package and `completion-preview-mode' in tandem.
|
||||
|
||||
(defcustom prot-emacs-load-which-key nil
|
||||
"When non-nil, display key binding hints after a short delay.
|
||||
This user option must be set in the `prot-emacs-pre-custom.el'
|
||||
file. If that file exists in the Emacs directory, it is loaded
|
||||
before all other modules of my setup."
|
||||
Use the generic completions buffer if any other value is set."
|
||||
:group 'prot-emacs
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom prot-emacs-load-icons nil
|
||||
"When non-nil, enable iconography in various contexts.
|
||||
This installs and uses the `nerd-icons' package and its variants.
|
||||
NOTE that you still need to invoke `nerd-icons-install-fonts'
|
||||
manually to first get the icon files.
|
||||
|
||||
This user option must be set in the `prot-emacs-pre-custom.el'
|
||||
file. If that file exists in the Emacs directory, it is loaded
|
||||
before all other modules of my setup."
|
||||
:group 'prot-emacs
|
||||
:type 'boolean)
|
||||
:type '(choice
|
||||
(const :tag "Generic completions buffer" nil)
|
||||
(const :tag "Generic completions buffer" t)
|
||||
(const :tag "The `corfu' package" corfu)
|
||||
(const :tag "The `company' package" company)
|
||||
(const :tag "The built-in `completion-preview-mode'" completion-preview)
|
||||
(const :tag "Corfu and `completion-preview-mode'" corfu-completion-preview)))
|
||||
|
||||
(setq make-backup-files nil)
|
||||
(setq backup-inhibited nil) ; Not sure if needed, given `make-backup-files'
|
||||
(setq create-lockfiles nil)
|
||||
|
||||
;; Make native compilation silent and prune its cache.
|
||||
;; Make native compilation silent.
|
||||
(when (native-comp-available-p)
|
||||
(setq native-comp-async-report-warnings-errors 'silent) ; Emacs 28 with native compilation
|
||||
(setq native-compile-prune-cache t)) ; Emacs 29
|
||||
(setq native-comp-async-report-warnings-errors 'silent))
|
||||
|
||||
;; Disable the damn thing by making it disposable.
|
||||
(setq custom-file (make-temp-file "emacs-custom-"))
|
||||
|
|
@ -93,7 +84,7 @@ before all other modules of my setup."
|
|||
(mapc
|
||||
(lambda (command)
|
||||
(put command 'disabled nil))
|
||||
'(list-timers narrow-to-region narrow-to-page upcase-region downcase-region))
|
||||
'(list-timers narrow-to-region narrow-to-page upcase-region downcase-region diff-restrict-view))
|
||||
|
||||
;; And disable these
|
||||
(mapc
|
||||
|
|
@ -101,8 +92,14 @@ before all other modules of my setup."
|
|||
(put command 'disabled t))
|
||||
'(eshell project-eshell overwrite-mode iconify-frame diary))
|
||||
|
||||
;; Always start with *scratch*
|
||||
(setq initial-buffer-choice t)
|
||||
(setq initial-major-mode 'lisp-interaction-mode)
|
||||
(setq initial-scratch-message
|
||||
(format ";; This is `%s'. Type `%s' to evaluate and print results.\n\n"
|
||||
'lisp-interaction-mode
|
||||
(propertize
|
||||
(substitute-command-keys "\\<lisp-interaction-mode-map>\\[eval-print-last-sexp]")
|
||||
'face 'help-key-binding)))
|
||||
|
||||
(mapc
|
||||
(lambda (string)
|
||||
|
|
@ -139,17 +136,30 @@ before all other modules of my setup."
|
|||
'(agitate
|
||||
altcaps
|
||||
beframe
|
||||
buffer-to-pdf
|
||||
consult-denote
|
||||
cursory
|
||||
denote
|
||||
denote-journal
|
||||
denote-markdown
|
||||
denote-merge
|
||||
denote-org
|
||||
denote-sequence
|
||||
denote-silo
|
||||
dired-preview
|
||||
doric-themes
|
||||
ef-themes
|
||||
fontaine
|
||||
gnome-accent-theme-switcher
|
||||
institution-calendar
|
||||
lin
|
||||
logos
|
||||
mct
|
||||
modus-themes
|
||||
notmuch-indicator
|
||||
oxford-calendar
|
||||
pulsar
|
||||
show-font
|
||||
spacious-padding
|
||||
standard-themes
|
||||
substitute
|
||||
|
|
@ -160,10 +170,37 @@ before all other modules of my setup."
|
|||
|
||||
;; Also read: <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>
|
||||
(setq package-pinned-packages
|
||||
`(,@(mapcar
|
||||
(lambda (package)
|
||||
(cons package "gnu-elpa-devel"))
|
||||
prot-emacs-my-packages)))
|
||||
(mapcar
|
||||
(lambda (package)
|
||||
(cons package "gnu-elpa-devel"))
|
||||
prot-emacs-my-packages))
|
||||
|
||||
;; These are for Emacs 31.
|
||||
(setq package-review-policy
|
||||
(delq nil
|
||||
(append
|
||||
(mapcar
|
||||
(lambda (package)
|
||||
(list 'not 'package package))
|
||||
prot-emacs-my-packages)
|
||||
(mapcar
|
||||
(lambda (archive)
|
||||
(let ((archive-name (car archive)))
|
||||
(unless (string= archive-name "gnu-elpa-devel")
|
||||
(cons 'archive archive-name))))
|
||||
package-archives))))
|
||||
|
||||
(setq package-review-diff-command
|
||||
(cons diff-command
|
||||
'("-u"
|
||||
"-x" "'*.elc'"
|
||||
"-x" "'*-autoloads.el'"
|
||||
"-x" "'*-pkg.el'"
|
||||
"-x" "'*.info'"
|
||||
"-x" "'*.texi'"
|
||||
"-x" "'*.txt'"
|
||||
"-x" "'*.md'"
|
||||
"-x" "'*.org'")))
|
||||
|
||||
(setq custom-safe-themes t)
|
||||
|
||||
|
|
@ -178,10 +215,10 @@ effects."
|
|||
(declare (indent defun))
|
||||
(let ((eval))
|
||||
(dolist (element body)
|
||||
(when-let (((plistp element))
|
||||
(key (car element))
|
||||
((eq key :eval))
|
||||
(val (cadr element)))
|
||||
(when-let* (((plistp element))
|
||||
(key (car element))
|
||||
((eq key :eval))
|
||||
(val (cadr element)))
|
||||
(setq eval val
|
||||
body (delq element body))))
|
||||
(when eval `(progn ,@body))))
|
||||
|
|
@ -199,6 +236,74 @@ effects."
|
|||
(indent-region beg end))
|
||||
(user-error "No active region; will not insert `prot-emacs-comment' here"))))
|
||||
|
||||
(defmacro prot-emacs-install (package)
|
||||
"Install PACKAGE, unless it is installed."
|
||||
(declare (indent 0))
|
||||
(unless (symbolp package)
|
||||
(error "The package `%s' is not a symbol" package))
|
||||
`(unless (package-installed-p ',package)
|
||||
(condition-case-unless-debug nil
|
||||
(progn
|
||||
(unless package-archive-contents
|
||||
(package-refresh-contents))
|
||||
(package-install ',package))
|
||||
(error (message "Cannot install `%s'; try `M-x package-refresh-contents' first" ',package)))))
|
||||
|
||||
(defmacro prot-emacs-install-vc (package &rest vc-args)
|
||||
"Install PACKAGE with VC-ARGS, unless it is installed."
|
||||
(declare (indent 0))
|
||||
(unless (symbolp package)
|
||||
(error "The package `%s' is not a symbol" package))
|
||||
`(progn
|
||||
(unless (package-installed-p ',package)
|
||||
(condition-case-unless-debug nil
|
||||
,(cond
|
||||
((length= vc-args 0)
|
||||
`(error "VC-ARGS must be 1 or more: `%S'" ',vc-args))
|
||||
((length> vc-args 1)
|
||||
`(apply 'package-vc-install ,(append (list package) vc-args)))
|
||||
(t
|
||||
`(funcall 'package-vc-install ,(car vc-args))))
|
||||
(error (message "Cannot install `%s' using VC-ARGS `%S'" ',package ',vc-args))))))
|
||||
|
||||
(defmacro prot-emacs-hook (hooks functions &optional remove after)
|
||||
"For each HOOKS `add-hook' the FUNCTIONS.
|
||||
With optional REMOVE as non-nil, then `remove-hook' the FUNCTIONS from
|
||||
HOOKS.
|
||||
|
||||
With optional AFTER as the unquoted symbol of a feature, do so after the
|
||||
given feature is available."
|
||||
(declare (indent 0))
|
||||
(cond
|
||||
((symbolp hooks)
|
||||
(setq hooks (list hooks)))
|
||||
((not (proper-list-p hooks))
|
||||
(error "The hooks are not a list: `%S'" hooks)))
|
||||
(cond
|
||||
((symbolp functions)
|
||||
(setq functions (list functions)))
|
||||
((not (proper-list-p functions))
|
||||
(error "The functions are not a list: `%S'" functions)))
|
||||
(let* ((fn (if remove 'remove-hook 'add-hook))
|
||||
(body (mapcar
|
||||
(lambda (h)
|
||||
(mapcar
|
||||
(lambda (f) `(,fn ',h #',f))
|
||||
functions))
|
||||
hooks))
|
||||
(hooks nil))
|
||||
(dolist (element body)
|
||||
(dolist (hook element)
|
||||
(push hook hooks)))
|
||||
(setq hooks (nreverse hooks))
|
||||
(cond
|
||||
(after
|
||||
`(with-eval-after-load ',after ,@hooks))
|
||||
((length> hooks 1)
|
||||
`(progn ,@hooks))
|
||||
(t
|
||||
(car hooks)))))
|
||||
|
||||
(defmacro prot-emacs-keybind (keymap &rest definitions)
|
||||
"Expand key binding DEFINITIONS for the given KEYMAP.
|
||||
DEFINITIONS is a sequence of string and command pairs."
|
||||
|
|
@ -208,8 +313,8 @@ DEFINITIONS is a sequence of string and command pairs."
|
|||
(let ((keys (seq-filter #'stringp definitions))
|
||||
;; We do accept nil as a definition: it unsets the given key.
|
||||
(commands (seq-remove #'stringp definitions)))
|
||||
`(when-let (((keymapp ,keymap))
|
||||
(map ,keymap))
|
||||
`(when-let* (((keymapp ,keymap))
|
||||
(map ,keymap))
|
||||
,@(mapcar
|
||||
(lambda (pair)
|
||||
(let* ((key (car pair))
|
||||
|
|
@ -228,6 +333,19 @@ DEFINITIONS is a sequence of string and command pairs."
|
|||
;; "C-c b" beframe-prefix-map
|
||||
;; "C-x k" #'kill-buffer)
|
||||
|
||||
(defmacro prot-emacs-autoload (functions file)
|
||||
"Declare autoloads for FUNCTIONS for FILE."
|
||||
(declare (indent 0))
|
||||
(when (symbolp functions)
|
||||
(setq functions (list functions)))
|
||||
(unless (listp functions)
|
||||
(error "The functions must be a list or symbol: %S" functions))
|
||||
(unless (stringp file)
|
||||
(error "The file must be a string: %S" file))
|
||||
(if (length> functions 1)
|
||||
`(progn ,@(mapcar (lambda (f) `(autoload #',f ,file)) functions))
|
||||
`(autoload #',(car functions) ,file)))
|
||||
|
||||
(defmacro prot-emacs-abbrev (table &rest definitions)
|
||||
"Expand abbrev DEFINITIONS for the given TABLE.
|
||||
DEFINITIONS is a sequence of (i) string pairs mapping the
|
||||
|
|
@ -245,24 +363,19 @@ making an abbreviation to a function."
|
|||
(if (stringp expansion)
|
||||
`(define-abbrev ,table ,abbrev ,expansion)
|
||||
`(define-abbrev ,table ,abbrev "" ,expansion))))
|
||||
(seq-split definitions 2)))
|
||||
(seq-split definitions 2))
|
||||
;; Allow abbrevs with a prefix colon, semicolon, or underscore. I demonstrated
|
||||
;; this here: <https://protesilaos.com/codelog/2024-02-03-emacs-abbrev-mode/>.
|
||||
(abbrev-table-put ,table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
|
||||
(error "%s is not an abbrev table" ,table)))
|
||||
|
||||
(defvar prot-emacs-package-form-regexp
|
||||
"^(\\(prot-emacs-keybind\\|prot-emacs-abbrev\\) +'?\\([0-9a-zA-Z-]+\\)"
|
||||
"Regexp to add packages to `lisp-imenu-generic-expression'.")
|
||||
|
||||
(eval-after-load 'lisp-mode
|
||||
`(add-to-list 'lisp-imenu-generic-expression
|
||||
(list "Packages" ,prot-emacs-package-form-regexp 2)))
|
||||
|
||||
(defconst prot-emacs-font-lock-keywords
|
||||
'(("(\\(prot-emacs-\\(keybind\\|abbrev\\)\\)\\_>[ \t']*\\(\\(\\sw\\|\\s_\\)+\\)?"
|
||||
(3 font-lock-variable-name-face nil t))
|
||||
("(\\(prot-emacs-comment\\)\\_>[ \t']*"
|
||||
(1 font-lock-preprocessor-face nil t))))
|
||||
|
||||
(font-lock-add-keywords 'emacs-lisp-mode prot-emacs-font-lock-keywords)
|
||||
(defmacro prot-emacs-configure (&rest body)
|
||||
"Evaluate BODY and catch any errors."
|
||||
(declare (indent 0))
|
||||
`(condition-case err
|
||||
(progn ,@body)
|
||||
((error user-error quit)
|
||||
(message "Failed to configure package starting with `%S' because of `%S'" (car ',body) (cdr err)))))
|
||||
|
||||
;; For those who use my dotfiles and need an easy way to write their
|
||||
;; own extras on top of what I already load. The file must exist at
|
||||
|
|
@ -272,6 +385,15 @@ making an abbreviation to a function."
|
|||
;; preferences BEFORE loading any of the modules.
|
||||
(load (locate-user-emacs-file "prot-emacs-pre-custom.el") :no-error :no-message)
|
||||
|
||||
(defvar prot-display-graphic-p (display-graphic-p)
|
||||
"When non-nil, the display is graphical.")
|
||||
|
||||
(defun prot-emacs-gnome-prefers-dark-p ()
|
||||
"Return non-nil if GNOME color-scheme is set to dark."
|
||||
(string-match-p
|
||||
"dark"
|
||||
(shell-command-to-string "gsettings get org.gnome.desktop.interface color-scheme")))
|
||||
|
||||
(require 'prot-emacs-theme)
|
||||
(require 'prot-emacs-essentials)
|
||||
(require 'prot-emacs-modeline)
|
||||
|
|
@ -284,17 +406,13 @@ making an abbreviation to a function."
|
|||
(require 'prot-emacs-langs)
|
||||
(require 'prot-emacs-email)
|
||||
(require 'prot-emacs-web)
|
||||
(when prot-emacs-load-which-key
|
||||
(require 'prot-emacs-which-key))
|
||||
(when prot-emacs-load-icons
|
||||
(require 'prot-emacs-icons))
|
||||
(require 'prot-emacs-which-key)
|
||||
(require 'prot-emacs-icons)
|
||||
|
||||
;; For those who use my dotfiles and need an easy way to write their
|
||||
;; own extras on top of what I already load. The file must exist at
|
||||
;; ~/.emacs.d/prot-emacs-post-custom.el
|
||||
;;
|
||||
;; The purpose of the "post customisations" is to make tweaks to what
|
||||
;; I already define, such as to change the default theme. See above
|
||||
;; for the `prot-emacs-pre-custom.el' to make changes BEFORE loading
|
||||
;; any of my other configurations.
|
||||
;; The purpose of the "post customisations" is to evaluate arbitrary
|
||||
;; code AFTER loading all my configurations.
|
||||
(load (locate-user-emacs-file "prot-emacs-post-custom.el") :no-error :no-message)
|
||||
|
|
|
|||
|
|
@ -1,190 +1,158 @@
|
|||
;;; General minibuffer settings
|
||||
(use-package minibuffer
|
||||
:ensure nil
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
;;;; Completion styles
|
||||
(setq completion-styles '(basic substring initials flex orderless)) ; also see `completion-category-overrides'
|
||||
(setq completion-styles '(basic substring initials partial-completion flex)) ; also see `completion-category-overrides'
|
||||
(setq completion-flex-nospace t)
|
||||
(setq completion-pcm-leading-wildcard nil) ; Emacs 31
|
||||
(with-eval-after-load 'orderless
|
||||
(setq completion-styles (append completion-styles '(orderless)))))
|
||||
|
||||
;;;; Completion category overrides
|
||||
(prot-emacs-configure
|
||||
;; Reset all the per-category defaults so that (i) we use the
|
||||
;; standard `completion-styles' and (ii) can specify our own styles
|
||||
;; in the `completion-category-overrides' without having to
|
||||
;; explicitly override everything.
|
||||
(setq completion-category-defaults nil)
|
||||
|
||||
;; A non-exhaustve list of known completion categories:
|
||||
;;
|
||||
;; - `bookmark'
|
||||
;; - `buffer'
|
||||
;; - `charset'
|
||||
;; - `coding-system'
|
||||
;; - `color'
|
||||
;; - `command' (e.g. `M-x')
|
||||
;; - `customize-group'
|
||||
;; - `environment-variable'
|
||||
;; - `expression'
|
||||
;; - `face'
|
||||
;; - `file'
|
||||
;; - `function' (the `describe-function' command bound to `C-h f')
|
||||
;; - `info-menu'
|
||||
;; - `imenu'
|
||||
;; - `input-method'
|
||||
;; - `kill-ring'
|
||||
;; - `library'
|
||||
;; - `minor-mode'
|
||||
;; - `multi-category'
|
||||
;; - `package'
|
||||
;; - `project-file'
|
||||
;; - `symbol' (the `describe-symbol' command bound to `C-h o')
|
||||
;; - `theme'
|
||||
;; - `unicode-name' (the `insert-char' command bound to `C-x 8 RET')
|
||||
;; - `variable' (the `describe-variable' command bound to `C-h v')
|
||||
;; - `consult-grep'
|
||||
;; - `consult-isearch'
|
||||
;; - `consult-kmacro'
|
||||
;; - `consult-location'
|
||||
;; - `embark-keybinding'
|
||||
;;
|
||||
(setq completion-category-overrides
|
||||
;; NOTE 2021-10-25: I am adding `basic' because it works better as a
|
||||
;; default for some contexts. Read:
|
||||
;; <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=50387>.
|
||||
;;
|
||||
;; `partial-completion' is a killer app for files, because it
|
||||
;; can expand ~/.l/s/fo to ~/.local/share/fonts.
|
||||
;;
|
||||
;; If `basic' cannot match my current input, Emacs tries the
|
||||
;; next completion style in the given order. In other words,
|
||||
;; `orderless' kicks in as soon as I input a space or one of its
|
||||
;; style dispatcher characters.
|
||||
'((file (styles . (basic partial-completion orderless)))
|
||||
(bookmark (styles . (basic substring)))
|
||||
(library (styles . (basic substring)))
|
||||
(embark-keybinding (styles . (basic substring)))
|
||||
(imenu (styles . (basic substring orderless)))
|
||||
(consult-location (styles . (basic substring orderless)))
|
||||
(kill-ring (styles . (emacs22 orderless)))
|
||||
(eglot (styles . (emacs22 substring orderless))))))
|
||||
(prot-minibuffer-missing-categories-mode 1)
|
||||
|
||||
;; NOTE 2025-12-02: The `eager-display' and `eager-update' are part of Emacs 31.
|
||||
(let* ((eager-both '((eager-display . t)
|
||||
(eager-update . t)))
|
||||
(eager-both-no-sort (append eager-both (list (cons 'display-sort-function #'identity))))
|
||||
(eager-update-only '((eager-display . nil)
|
||||
(eager-update . t)))
|
||||
(eager-update-only-no-sort (append eager-update-only (list (cons 'display-sort-function #'identity)))))
|
||||
(setq completion-category-overrides
|
||||
`((file . (,@eager-update-only
|
||||
(styles . (partial-completion))
|
||||
(group-function . ,#'prot-minibuffer-file-group)
|
||||
(affixation-function . ,#'prot-minibuffer-file-affixate)
|
||||
(display-sort-function . ,#'prot-minibuffer-file-sort)))
|
||||
(buffer . (,@eager-update-only
|
||||
(affixation-function . ,#'prot-minibuffer-buffer-affixate)))
|
||||
(project-file . (,@eager-update-only
|
||||
(group-function . ,#'prot-minibuffer-file-group)
|
||||
(affixation-function . ,#'prot-minibuffer-file-affixate)))
|
||||
(prot-minibuffer-library . (,@eager-update-only
|
||||
(annotation-function . ,#'prot-minibuffer-library-annotate)
|
||||
(display-sort-function . ,#'prot-minibuffer-library-sort)))
|
||||
(bookmark . (,@eager-update-only
|
||||
(affixation-function . ,#'prot-minibuffer-bookmark-affixate)))
|
||||
(command . (,@eager-update-only
|
||||
(affixation-function . nil) ; so that the `annotation-function' can take effect
|
||||
(annotation-function . ,#'prot-minibuffer-command-annotate)))
|
||||
(prot-minibuffer-input-method . (,@eager-update-only
|
||||
(group-function . ,#'prot-minibuffer-input-method-group)
|
||||
(display-sort-function . ,#'prot-minibuffer-input-method-sort)))
|
||||
(symbol-help . ,eager-update-only)
|
||||
(denote-file . ,eager-update-only)
|
||||
(cape-abbrev . ,eager-update-only)
|
||||
(cape-dabbrev . ,eager-update-only)
|
||||
(cape-dict . ,eager-update-only)
|
||||
(cape-emoji . ,eager-update-only)
|
||||
(cape-history . ,eager-update-only)
|
||||
(cape-keyword . ,eager-update-only)
|
||||
(cape-super . ,eager-update-only)
|
||||
(tmr-timer . ,eager-both)
|
||||
(prot-minibuffer-emoji . ,eager-update-only)
|
||||
(theme . ,eager-update-only)
|
||||
(unicode-name . ,eager-update-only)
|
||||
(prot-minibuffer-pass . ,eager-update-only)
|
||||
(imenu . ,eager-update-only-no-sort)
|
||||
(consult-location . ,eager-update-only-no-sort)
|
||||
(prot-minibuffer-kill-ring . ,eager-update-only-no-sort)))))
|
||||
|
||||
;;; Orderless completion style (and prot-orderless.el)
|
||||
(use-package orderless
|
||||
:ensure t
|
||||
:demand t
|
||||
:after minibuffer
|
||||
:config
|
||||
;; Remember to check my `completion-styles' and the
|
||||
;; `completion-category-overrides'.
|
||||
(setq orderless-matching-styles '(orderless-prefixes orderless-regexp))
|
||||
(when prot-emacs-completion-extras
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install orderless)
|
||||
(require 'orderless)
|
||||
;; Remember to check my `completion-styles' and the
|
||||
;; `completion-category-overrides'.
|
||||
(setq orderless-matching-styles '(orderless-prefixes orderless-regexp))
|
||||
(setq orderless-smart-case nil)
|
||||
|
||||
;; SPC should never complete: use it for `orderless' groups.
|
||||
;; The `?' is a regexp construct.
|
||||
:bind ( :map minibuffer-local-completion-map
|
||||
("SPC" . nil)
|
||||
("?" . nil)))
|
||||
|
||||
(use-package prot-orderless
|
||||
:ensure nil
|
||||
:config
|
||||
(setq orderless-style-dispatchers
|
||||
'(prot-orderless-literal
|
||||
prot-orderless-file-ext
|
||||
prot-orderless-beg-or-end)))
|
||||
;; SPC should never complete: use it for `orderless' groups.
|
||||
;; The `?' is a regexp construct.
|
||||
(prot-emacs-keybind minibuffer-local-completion-map
|
||||
"SPC" nil
|
||||
"?" nil)))
|
||||
|
||||
(setq completion-ignore-case t)
|
||||
(setq read-buffer-completion-ignore-case t)
|
||||
(setq-default case-fold-search t) ; For general regexp
|
||||
(setq read-file-name-completion-ignore-case t)
|
||||
(setq minibuffer-history-case-insensitive-variables t)
|
||||
|
||||
(use-package mb-depth
|
||||
:ensure nil
|
||||
:hook (after-init . minibuffer-depth-indicate-mode)
|
||||
:config
|
||||
(setq read-minibuffer-restore-windows nil) ; Emacs 28
|
||||
(setq enable-recursive-minibuffers t))
|
||||
(prot-emacs-configure
|
||||
(setq read-minibuffer-restore-windows nil)
|
||||
(setq enable-recursive-minibuffers t) ; Emacs 28
|
||||
(minibuffer-depth-indicate-mode 1))
|
||||
|
||||
(use-package minibuf-eldef
|
||||
:ensure nil
|
||||
:hook (after-init . minibuffer-electric-default-mode)
|
||||
:config
|
||||
(setq minibuffer-default-prompt-format " [%s]")) ; Emacs 29
|
||||
|
||||
(use-package rfn-eshadow
|
||||
:ensure nil
|
||||
:hook (minibuffer-setup . cursor-intangible-mode)
|
||||
:config
|
||||
;; Not everything here comes from rfn-eshadow.el, but this is fine.
|
||||
(prot-emacs-configure
|
||||
(setq minibuffer-default-prompt-format " [%s]") ; Emacs 29
|
||||
(minibuffer-electric-default-mode 1))
|
||||
|
||||
(prot-emacs-configure
|
||||
(setq resize-mini-windows t)
|
||||
(setq read-answer-short t) ; also check `use-short-answers' for Emacs28
|
||||
(setq echo-keystrokes 0.25)
|
||||
(setq kill-ring-max 60) ; Keep it small
|
||||
|
||||
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
|
||||
;; Do not allow the cursor to move inside the minibuffer prompt. I
|
||||
;; got this from the documentation of Daniel Mendler's Vertico
|
||||
;; package: <https://github.com/minad/vertico>.
|
||||
(setq minibuffer-prompt-properties
|
||||
'(read-only t cursor-intangible t face minibuffer-prompt))
|
||||
|
||||
;; MCT has a variant of this built-in.
|
||||
(unless (eq prot-emacs-completion-ui 'mct)
|
||||
;; Add prompt indicator to `completing-read-multiple'. We display
|
||||
;; [`completing-read-multiple': <separator>], e.g.,
|
||||
;; [`completing-read-multiple': ,] if the separator is a comma. This
|
||||
;; is adapted from the README of the `vertico' package by Daniel
|
||||
;; Mendler. I made some small tweaks to propertize the segments of
|
||||
;; the prompt.
|
||||
(defun crm-indicator (args)
|
||||
(cons (format "[`completing-read-multiple': %s] %s"
|
||||
(propertize
|
||||
(replace-regexp-in-string
|
||||
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
|
||||
crm-separator)
|
||||
'face 'error)
|
||||
(car args))
|
||||
(cdr args)))
|
||||
|
||||
(advice-add #'completing-read-multiple :filter-args #'crm-indicator))
|
||||
(setq crm-prompt (format "%s %%p" (propertize "[%d]" 'face 'shadow))) ; Emacs 31
|
||||
|
||||
(file-name-shadow-mode 1))
|
||||
|
||||
(use-package minibuffer
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(setq completions-format 'one-column)
|
||||
(setq completion-show-help nil)
|
||||
(setq completion-auto-help 'always)
|
||||
(setq completion-auto-select nil)
|
||||
(setq completions-detailed t)
|
||||
(setq completion-show-inline-help nil)
|
||||
(setq completions-max-height 6)
|
||||
(setq completions-header-format (propertize "%s candidates:\n" 'face 'bold-italic))
|
||||
(setq completions-highlight-face 'completions-highlight)
|
||||
(setq minibuffer-completion-auto-choose t)
|
||||
(setq minibuffer-visible-completions t) ; Emacs 30
|
||||
(setq completions-sort 'historical)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-hook
|
||||
(completion-list-mode-hook minibuffer-setup-hook)
|
||||
prot-common-truncate-lines-silently)
|
||||
|
||||
(setq completions-group-format
|
||||
(concat
|
||||
(propertize (make-string 20 ? ) 'face 'completions-group-separator)
|
||||
(propertize " %s " 'face 'completions-group-title)
|
||||
(propertize " " 'face 'completions-group-separator 'display '(space :align-to right))))
|
||||
|
||||
(unless prot-emacs-completion-ui
|
||||
(prot-emacs-keybind minibuffer-local-completion-map
|
||||
"<up>" #'minibuffer-previous-line-completion
|
||||
"<down>" #'minibuffer-next-line-completion)
|
||||
(prot-minibuffer-completions-mode 1)
|
||||
|
||||
(add-hook 'completion-list-mode-hook #'prot-common-truncate-lines-silently)))
|
||||
(prot-emacs-keybind completion-in-region-mode-map
|
||||
"M-n" #'minibuffer-next-completion
|
||||
"M-p" #'minibuffer-previous-completion)
|
||||
|
||||
(prot-emacs-keybind minibuffer-local-completion-map
|
||||
"<down>" #'minibuffer-next-line-completion
|
||||
"<up>" #'minibuffer-previous-line-completion
|
||||
"C-h C-h" #'prot-minibuffer-completions-describe-at-point ; overrides `help-for-help'
|
||||
"C-<tab>" #'prot-minibuffer-choose-completion-no-exit
|
||||
"RET" #'prot-minibuffer-choose-completion-exit)
|
||||
|
||||
(prot-emacs-keybind completion-list-mode-map
|
||||
"C-h C-h" #'prot-minibuffer-completions-describe-at-point ; overrides `help-for-help'
|
||||
"C-<tab>" #'prot-minibuffer-choose-completion-no-exit
|
||||
"RET" #'prot-minibuffer-choose-completion-dwim)))
|
||||
|
||||
;;;; `savehist' (minibuffer and related histories)
|
||||
(use-package savehist
|
||||
:ensure nil
|
||||
:hook (after-init . savehist-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq savehist-file (locate-user-emacs-file "savehist"))
|
||||
(setq history-length 100)
|
||||
(setq history-delete-duplicates t)
|
||||
(setq savehist-save-minibuffer-history t)
|
||||
(add-to-list 'savehist-additional-variables 'kill-ring))
|
||||
(with-eval-after-load 'savehist
|
||||
(add-to-list 'savehist-additional-variables 'kill-ring))
|
||||
(savehist-mode 1))
|
||||
|
||||
(use-package dabbrev
|
||||
:ensure nil
|
||||
:commands (dabbrev-expand dabbrev-completion)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
;;;; `dabbrev' (dynamic word completion (dynamic abbreviations))
|
||||
(setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
|
||||
(setq dabbrev-abbrev-skip-leading-regexp "[$*/=~']")
|
||||
|
|
@ -195,31 +163,41 @@
|
|||
(setq dabbrev-check-other-buffers t)
|
||||
(setq dabbrev-eliminate-newlines t)
|
||||
(setq dabbrev-upcase-means-case-search t)
|
||||
(setq dabbrev-ignored-buffer-modes
|
||||
'(archive-mode image-mode docview-mode pdf-view-mode)))
|
||||
(setq dabbrev-ignored-buffer-modes '(archive-mode image-mode docview-mode pdf-view-mode)))
|
||||
|
||||
;;;; `abbrev' (Abbreviations, else Abbrevs)
|
||||
(use-package abbrev
|
||||
:ensure nil
|
||||
;; message-mode derives from text-mode, so we don't need a separate
|
||||
;; hook for it.
|
||||
:hook ((text-mode prog-mode git-commit-mode) . abbrev-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(require 'prot-abbrev)
|
||||
|
||||
(setq only-global-abbrevs nil)
|
||||
|
||||
(define-key global-map (kbd "C-x a u") #'unexpand-abbrev)
|
||||
|
||||
;; Because the *scratch* buffer is produced before we load this, we
|
||||
;; have to explicitly activate the mode there.
|
||||
(when-let* ((scratch (get-buffer "*scratch*")))
|
||||
(with-current-buffer scratch
|
||||
(abbrev-mode 1)))
|
||||
|
||||
;; By default, abbrev asks for confirmation on whether to use
|
||||
;; `abbrev-file-name' to save abbrevations. I do not need that, nor
|
||||
;; do I want it.
|
||||
(remove-hook 'save-some-buffers-functions #'abbrev--possibly-save)
|
||||
|
||||
(prot-emacs-hook
|
||||
( text-mode-hook prog-mode-hook git-commit-mode-hook
|
||||
vc-git-log-edit-mode-hook message-mode-hook)
|
||||
abbrev-mode)
|
||||
|
||||
(prot-emacs-abbrev global-abbrev-table
|
||||
"meweb" "https://protesilaos.com"
|
||||
"megit" "https://github.com/protesilaos"
|
||||
"mehub" "https://github.com/protesilaos"
|
||||
"meclone" "git@github.com/protesilaos/"
|
||||
"melab" "https://gitlab.com/protesilaos"
|
||||
"medrive" "hyper://5cr7mxac8o8aymun698736tayrh1h4kbqf359cfk57swjke716gy/"
|
||||
";web" "https://protesilaos.com"
|
||||
";git" "https://github.com/protesilaos"
|
||||
";hub" "https://github.com/protesilaos"
|
||||
";clone" "git@github.com/protesilaos/"
|
||||
";lab" "https://gitlab.com/protesilaos"
|
||||
";drive" "hyper://5cr7mxac8o8aymun698736tayrh1h4kbqf359cfk57swjke716gy/")
|
||||
";time" #'prot-abbrev-current-time
|
||||
";date" #'prot-abbrev-current-date
|
||||
";jitsi" #'prot-abbrev-jitsi-link)
|
||||
|
||||
(prot-emacs-abbrev text-mode-abbrev-table
|
||||
"asciidoc" "AsciiDoc"
|
||||
|
|
@ -239,24 +217,20 @@
|
|||
"texmacs" "TeXmacs"
|
||||
"typescript" "TypeScript"
|
||||
"visavis" "vis-à-vis"
|
||||
"deja" "déjà"
|
||||
"voila" "voilà"
|
||||
"youtube" "YouTube"
|
||||
";up" "🙃"
|
||||
";uni" "🦄"
|
||||
";laugh" "🤣"
|
||||
";smile" "😀"
|
||||
";sun" "☀️")
|
||||
|
||||
;; Allow abbrevs with a prefix colon, semicolon, or underscore. I demonstrated
|
||||
;; this here: <https://protesilaos.com/codelog/2024-02-03-emacs-abbrev-mode/>.
|
||||
(abbrev-table-put global-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)")
|
||||
|
||||
(with-eval-after-load 'text-mode
|
||||
(abbrev-table-put text-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
|
||||
";update" #'prot-abbrev-update-html)
|
||||
|
||||
(with-eval-after-load 'org
|
||||
(prot-emacs-abbrev org-mode-abbrev-table
|
||||
";dev" "{{{development-version}}}")
|
||||
(abbrev-table-put org-mode-abbrev-table :regexp "\\(?:^\\|[\t\s]+\\)\\(?1:[:;_].*\\|.*\\)"))
|
||||
";dev" "{{{development-version}}}"
|
||||
";key" #'prot-abbrev-org-macro-key
|
||||
";cmd" #'prot-abbrev-org-macro-key-command))
|
||||
|
||||
(with-eval-after-load 'message
|
||||
(prot-emacs-abbrev message-mode-abbrev-table
|
||||
|
|
@ -265,79 +239,184 @@
|
|||
"niceday" "Have a nice day,\nProtesilaos (or simply \"Prot\")"
|
||||
"abest" "All the best,\nProt"
|
||||
"bregards" "Best regards,\nProt"
|
||||
"nday" "Have a nice day,\nProt"
|
||||
"nosrht" "P.S. I am phasing out SourceHut: <https://protesilaos.com/codelog/2024-01-27-sourcehut-no-more/>.
|
||||
Development continues on GitHub with GitLab as a mirror."))
|
||||
"nday" "Have a nice day,\nProt"))
|
||||
|
||||
;; The `prot-emacs-abbrev' macro, which simplifies how we use
|
||||
;; `define-abbrev', does not only expand a static text. It can take
|
||||
;; a pair of string and function to trigger the latter when the
|
||||
;; former is inserted. Think of it like the basis of a simplistic
|
||||
;; templating system.
|
||||
(require 'prot-abbrev)
|
||||
(prot-emacs-abbrev global-abbrev-table
|
||||
"metime" #'prot-abbrev-current-time
|
||||
"medate" #'prot-abbrev-current-date
|
||||
"mejitsi" #'prot-abbrev-jitsi-link
|
||||
";time" #'prot-abbrev-current-time
|
||||
";date" #'prot-abbrev-current-date
|
||||
";jitsi" #'prot-abbrev-jitsi-link)
|
||||
(with-eval-after-load 'markdown-mode
|
||||
(prot-emacs-abbrev markdown-mode-abbrev-table
|
||||
";vlog" "---
|
||||
title: \"#\"
|
||||
excerpt: \"#\"
|
||||
layout: vlog
|
||||
mediaid: \"#\"
|
||||
---
|
||||
"
|
||||
";post" "---
|
||||
title: \"#\"
|
||||
excerpt: \"#\"
|
||||
---
|
||||
"
|
||||
";poem" "---
|
||||
title: \"#\"
|
||||
excerpt: \"Just read the poem. No further comment.\"
|
||||
---
|
||||
"))
|
||||
|
||||
(prot-emacs-abbrev text-mode-abbrev-table
|
||||
";update" #'prot-abbrev-update-html)
|
||||
|
||||
;; Because the *scratch* buffer is produced before we load this, we
|
||||
;; have to explicitly activate the mode there.
|
||||
(when-let ((scratch (get-buffer "*scratch*")))
|
||||
(with-current-buffer scratch
|
||||
(abbrev-mode 1)))
|
||||
|
||||
;; By default, abbrev asks for confirmation on whether to use
|
||||
;; `abbrev-file-name' to save abbrevations. I do not need that, nor
|
||||
;; do I want it.
|
||||
(remove-hook 'save-some-buffers-functions #'abbrev--possibly-save))
|
||||
(with-eval-after-load 'vc-git
|
||||
(prot-emacs-abbrev vc-git-log-edit-mode-abbrev-table
|
||||
";update" #'prot-abbrev-update-html)))
|
||||
|
||||
;;; Corfu (in-buffer completion popup)
|
||||
(use-package corfu
|
||||
:ensure t
|
||||
:hook (after-init . global-corfu-mode)
|
||||
;; I also have (setq tab-always-indent 'complete) for TAB to complete
|
||||
;; when it does not need to perform an indentation change.
|
||||
:bind (:map corfu-map ("<tab>" . corfu-complete))
|
||||
:config
|
||||
(setq corfu-preview-current nil)
|
||||
(setq corfu-min-width 20)
|
||||
(when (or (eq prot-emacs-completion-in-buffer 'corfu)
|
||||
(eq prot-emacs-completion-in-buffer 'corfu-completion-preview))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install corfu)
|
||||
|
||||
(setq corfu-popupinfo-delay '(1.25 . 0.5))
|
||||
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
|
||||
(setq corfu-preview-current nil)
|
||||
(setq corfu-min-width 20)
|
||||
|
||||
;; Sort by input history (no need to modify `corfu-sort-function').
|
||||
(with-eval-after-load 'savehist
|
||||
(corfu-history-mode 1)
|
||||
(add-to-list 'savehist-additional-variables 'corfu-history)))
|
||||
(setq corfu-popupinfo-delay '(1.25 . 0.5))
|
||||
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
|
||||
|
||||
(global-corfu-mode 1)
|
||||
|
||||
;; I also have (setq tab-always-indent 'complete) for TAB to complete
|
||||
;; when it does not need to perform an indentation change.
|
||||
(define-key corfu-map (kbd "<tab>") #'corfu-complete)
|
||||
|
||||
;; Sort by input history (no need to modify `corfu-sort-function').
|
||||
(with-eval-after-load 'savehist
|
||||
(corfu-history-mode 1)
|
||||
(add-to-list 'savehist-additional-variables 'corfu-history))))
|
||||
|
||||
(unless (eq prot-emacs-completion-in-buffer 'company)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install cape)
|
||||
|
||||
(defun prot/cape-super-set-local (capfs &optional individual-capfs)
|
||||
"Set `completion-at-point-functions' to current value plus CAPFS.
|
||||
Treat CAPFS and the default value as a super CAPF. Then append the
|
||||
INDIVIDUAL-CAPFS to the list."
|
||||
(let* ((all-for-super (append completion-at-point-functions capfs))
|
||||
(all-minus-global (delq t all-for-super))
|
||||
(cape-super (apply #'cape-capf-super all-minus-global)))
|
||||
(setq-local completion-at-point-functions (append (list cape-super) individual-capfs (list t)))))
|
||||
|
||||
(defun prot/cape-prog-setup ()
|
||||
"Set up Cape for programming."
|
||||
(prot/cape-super-set-local '(cape-dabbrev cape-abbrev) '(cape-file)))
|
||||
|
||||
(add-hook 'prog-mode-hook #'prot/cape-prog-setup)
|
||||
|
||||
(defun prot/cape-text-setup ()
|
||||
"Set up Cape for prose."
|
||||
(prot/cape-super-set-local '(cape-dict cape-dabbrev cape-abbrev cape-emoji) '(cape-file)))
|
||||
|
||||
(setq completion-at-point-functions '(cape-dabbrev cape-file))
|
||||
|
||||
(add-hook 'text-mode-hook #'prot/cape-text-setup)))
|
||||
|
||||
(when (eq prot-emacs-completion-in-buffer 'company)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install company)
|
||||
(prot-emacs-install company-statistics)
|
||||
|
||||
;; NOTE 2026-07-10: with the latest version of Company, (setq
|
||||
;; tab-always-indent 'complete) is supported out-of-the-box, which
|
||||
;; means that I can finally disable the automatic popup feature.
|
||||
(setq company-idle-delay nil)
|
||||
|
||||
(setq company-tooltip-limit 6)
|
||||
(setq company-tooltip-minimum-width 25)
|
||||
(setq company-tooltip-align-annotations t)
|
||||
(setq company-dabbrev-code-completion-styles t) ; use the `completion-styles'
|
||||
|
||||
(setq company-transformers '(company-sort-by-occurrence
|
||||
company-sort-by-backend-importance
|
||||
company-sort-prefer-same-case-prefix))
|
||||
|
||||
(setq company-frontends '(company-childframe-unless-just-one-frontend
|
||||
company-preview-if-just-one-frontend
|
||||
company-echo-metadata-frontend))
|
||||
|
||||
(setq company-backends '((company-capf company-dabbrev) company-files))
|
||||
|
||||
(define-advice company-show-doc-buffer (:around (&rest args) prot)
|
||||
(let ((help-window-select nil)
|
||||
(display-buffer-alist nil)
|
||||
(display-buffer-overriding-action
|
||||
'((display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(window-height . 0.3)
|
||||
(post-command-select-window . nil))))
|
||||
(apply args)))
|
||||
|
||||
(defun prot/company-prog-backends ()
|
||||
"Set `company-backends' locally to my preferred value for `prog-mode' and derivatives."
|
||||
(setq-local company-backends '((company-capf company-dabbrev company-abbrev) company-files)))
|
||||
|
||||
(defun prot/company-text-backends ()
|
||||
"Set `company-backends' locally to my preferred value for `text-mode' and derivatives."
|
||||
(setq-local company-backends '((company-dabbrev company-abbrev company-ispell company-capf) company-files)))
|
||||
|
||||
(prot-emacs-hook (prog-mode-hook comint-mode-hook) prot/company-prog-backends)
|
||||
(prot-emacs-hook (prog-mode-hook text-mode-hook comint-mode-hook) company-mode)
|
||||
(add-hook 'text-mode-hook #'prot/company-text-backends)
|
||||
|
||||
(with-eval-after-load 'company
|
||||
(prot-emacs-keybind company-active-map
|
||||
"C-s" #'company-filter-candidates
|
||||
"<escape>" #'company-abort
|
||||
"<tab>" #'company-complete-selection
|
||||
"<up>" #'company-select-previous-or-abort
|
||||
"<down>" #'company-select-next-or-abort)
|
||||
(company-statistics-mode 1))))
|
||||
|
||||
(when (or (eq prot-emacs-completion-in-buffer 'completion-preview)
|
||||
(eq prot-emacs-completion-in-buffer 'corfu-completion-preview))
|
||||
(prot-emacs-configure
|
||||
(setq completion-preview-exact-match-only nil)
|
||||
(setq completion-preview-minimum-symbol-length 2)
|
||||
(setq completion-preview-idle-delay nil)
|
||||
(setq completion-preview-ignore-case t)
|
||||
|
||||
(prot-emacs-hook
|
||||
(prog-mode-hook log-edit-mode-hook git-commit-mode-hook comint-mode-hook)
|
||||
completion-preview-mode)
|
||||
|
||||
(with-eval-after-load 'completion-preview
|
||||
(setq completion-preview-commands
|
||||
(seq-remove
|
||||
(lambda (command)
|
||||
(memq command '(delete-backward-char backward-delete-char-untabify)))
|
||||
completion-preview-commands))
|
||||
(prot-emacs-keybind completion-preview-active-mode-map
|
||||
"M-i" #'completion-preview-insert-word
|
||||
"M-n" #'completion-preview-next-candidate
|
||||
"M-p" #'completion-preview-prev-candidate
|
||||
"M-<return>" #'completion-preview-insert
|
||||
;; With TAB we effectively defer to another frontend to show
|
||||
;; more completion candidates at once.
|
||||
"<tab>" #'completion-preview-complete))
|
||||
|
||||
(with-eval-after-load 'org
|
||||
(add-to-list 'completion-preview-commands #'org-self-insert-command))))
|
||||
|
||||
;;; Enhanced minibuffer commands (consult.el)
|
||||
(when prot-emacs-completion-extras
|
||||
(use-package consult
|
||||
:ensure t
|
||||
:hook (completion-list-mode . consult-preview-at-point-mode)
|
||||
:bind
|
||||
( :map global-map
|
||||
("M-g M-g" . consult-goto-line)
|
||||
("M-K" . consult-keep-lines) ; M-S-k is similar to M-S-5 (M-%)
|
||||
("M-F" . consult-focus-lines) ; same principle
|
||||
("M-s M-b" . consult-buffer)
|
||||
("M-s M-f" . consult-find)
|
||||
("M-s M-g" . consult-grep)
|
||||
("M-s M-h" . consult-history)
|
||||
("M-s M-i" . consult-imenu)
|
||||
("M-s M-l" . consult-line)
|
||||
("M-s M-m" . consult-mark)
|
||||
("M-s M-y" . consult-yank-pop)
|
||||
("M-s M-s" . consult-outline)
|
||||
:map consult-narrow-map
|
||||
("?" . consult-narrow-help))
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install consult)
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"M-g M-g" #'consult-goto-line
|
||||
"M-s M-b" #'consult-buffer
|
||||
"M-s M-f" #'consult-find
|
||||
"M-s M-g" #'consult-grep
|
||||
"M-s M-h" #'consult-history
|
||||
"M-s M-i" #'consult-imenu
|
||||
"M-s M-l" #'consult-line
|
||||
"M-s M-m" #'consult-mark
|
||||
"M-s M-y" #'consult-yank-pop
|
||||
"M-s M-s" #'consult-outline)
|
||||
|
||||
(setq consult-line-numbers-widen t)
|
||||
;; (setq completion-in-region-function #'consult-completion-in-region)
|
||||
(setq consult-async-min-input 3)
|
||||
|
|
@ -349,123 +428,39 @@ Development continues on GitHub with GitLab as a mirror."))
|
|||
"-path */.git* -prune "
|
||||
"-or -path */.cache* -prune )"))
|
||||
(setq consult-preview-key 'any)
|
||||
(setq consult-project-function nil) ; always work from the current directory (use `cd' to switch directory)
|
||||
|
||||
(add-to-list 'consult-mode-histories '(vc-git-log-edit-mode . log-edit-comment-ring))
|
||||
;; see my `pulsar' package: <https://protesilaos.com/emacs/pulsar>
|
||||
(setq consult-after-jump-hook nil) ; reset it to avoid conflicts with my function
|
||||
(prot-emacs-hook
|
||||
consult-after-jump-hook
|
||||
(pulsar-recenter-top pulsar-reveal-entry)
|
||||
nil
|
||||
pulsar)))
|
||||
|
||||
(require 'consult-imenu) ; the `imenu' extension is in its own file
|
||||
|
||||
(with-eval-after-load 'pulsar
|
||||
;; see my `pulsar' package: <https://protesilaos.com/emacs/pulsar>
|
||||
(setq consult-after-jump-hook nil) ; reset it to avoid conflicts with my function
|
||||
(dolist (fn '(pulsar-recenter-top pulsar-reveal-entry))
|
||||
(add-hook 'consult-after-jump-hook fn)))))
|
||||
|
||||
;;; Extended minibuffer actions and more (embark.el and prot-embark.el)
|
||||
;;; Extended minibuffer actions and more (embark.el)
|
||||
(when prot-emacs-completion-extras
|
||||
(use-package embark
|
||||
:ensure t
|
||||
:defer 1
|
||||
:config
|
||||
(setq embark-confirm-act-all nil)
|
||||
;; The prot-embark.el has an advice to further simplify the
|
||||
;; minimal indicator. It shows cycling, which I never want to see
|
||||
;; or do.
|
||||
(setq embark-mixed-indicator-both nil)
|
||||
(setq embark-mixed-indicator-delay 1.0)
|
||||
(setq embark-indicators '(embark-mixed-indicator embark-highlight-indicator))
|
||||
(setq embark-verbose-indicator-nested nil) ; I think I don't have them, but I do not want them either
|
||||
(setq embark-verbose-indicator-buffer-sections '(bindings))
|
||||
(setq embark-verbose-indicator-excluded-actions
|
||||
'(embark-cycle embark-act-all embark-collect embark-export embark-insert))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install embark)
|
||||
|
||||
;; I never cycle and want to disable the damn thing. Normally, a
|
||||
;; nil value disables a key binding but here that value is
|
||||
;; interpreted as the binding for `embark-act'. So I just add
|
||||
;; some obscure key that I do not have. I absolutely do not want
|
||||
;; to cycle!
|
||||
(setq embark-cycle-key "<XF86Travel>")
|
||||
(add-hook 'embark-collect-mode-hook #'prot-common-truncate-lines-silently)
|
||||
|
||||
;; I do not want `embark-org' and am not sure what is loading it.
|
||||
;; So I just unsert all the keymaps... This is the nuclear option
|
||||
;; but here we are.
|
||||
(with-eval-after-load 'embark-org
|
||||
(defvar prot/embark-org-keymaps
|
||||
'(embark-org-table-cell-map
|
||||
embark-org-table-map
|
||||
embark-org-link-copy-map
|
||||
embark-org-link-map
|
||||
embark-org-src-block-map
|
||||
embark-org-item-map
|
||||
embark-org-plain-list-map
|
||||
embark-org-export-in-place-map)
|
||||
"List of Embark keymaps for Org.")
|
||||
(prot-emacs-keybind minibuffer-local-map
|
||||
"C-c C-c" #'embark-collect
|
||||
"C-c C-e" #'embark-export)
|
||||
|
||||
;; Reset `prot/embark-org-keymaps'.
|
||||
(seq-do
|
||||
(lambda (keymap)
|
||||
(set keymap (make-sparse-keymap)))
|
||||
prot/embark-org-keymaps)))
|
||||
;; Needed for correct exporting while using Embark with Consult commands.
|
||||
(prot-emacs-install embark-consult)
|
||||
|
||||
;; I define my own keymaps because I only use a few functions in a
|
||||
;; limited number of contexts.
|
||||
(use-package prot-embark
|
||||
:ensure nil
|
||||
:after embark
|
||||
:bind
|
||||
( :map global-map
|
||||
("C-," . prot-embark-act-no-quit)
|
||||
("C-." . prot-embark-act-quit)
|
||||
:map embark-collect-mode-map
|
||||
("C-," . prot-embark-act-no-quit)
|
||||
("C-." . prot-embark-act-quit)
|
||||
:map minibuffer-local-filename-completion-map
|
||||
("C-," . prot-embark-act-no-quit)
|
||||
("C-." . prot-embark-act-quit))
|
||||
:config
|
||||
(setq embark-keymap-alist
|
||||
'((buffer prot-embark-buffer-map)
|
||||
(command prot-embark-command-map)
|
||||
(expression prot-embark-expression-map)
|
||||
(file prot-embark-file-map)
|
||||
(function prot-embark-function-map)
|
||||
(identifier prot-embark-identifier-map)
|
||||
(package prot-embark-package-map)
|
||||
(region prot-embark-region-map)
|
||||
(symbol prot-embark-symbol-map)
|
||||
(url prot-embark-url-map)
|
||||
(variable prot-embark-variable-map)
|
||||
(t embark-general-map))))
|
||||
|
||||
;; Needed for correct exporting while using Embark with Consult
|
||||
;; commands.
|
||||
(use-package embark-consult
|
||||
:ensure t
|
||||
:after (embark consult)))
|
||||
(with-eval-after-load 'consult
|
||||
(require 'embark-consult))))
|
||||
|
||||
;;; Detailed completion annotations (marginalia.el)
|
||||
(use-package marginalia
|
||||
:ensure t
|
||||
:defer 1
|
||||
:config
|
||||
(setq marginalia-max-relative-age 0) ; absolute time
|
||||
(marginalia-mode 1))
|
||||
|
||||
;;;; Custom completion annotations
|
||||
(use-package prot-marginalia
|
||||
:ensure nil
|
||||
:after marginalia
|
||||
:config
|
||||
(setq marginalia-annotator-registry
|
||||
'((bookmark prot-marginalia-bookmark)
|
||||
(buffer prot-marginalia-buffer)
|
||||
(command marginalia-annotate-command)
|
||||
(function prot-marginalia-symbol)
|
||||
(symbol prot-marginalia-symbol)
|
||||
(variable prot-marginalia-symbol)
|
||||
(face marginalia-annotate-face)
|
||||
(imenu marginalia-annotate-imenu)
|
||||
(package prot-marginalia-package)
|
||||
(unicode-name marginalia-annotate-char))))
|
||||
(when prot-emacs-completion-extras
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install marginalia)
|
||||
(setq marginalia-max-relative-age 0) ; absolute time
|
||||
(marginalia-mode 1)))
|
||||
|
||||
;;; The minibuffer user interface (mct, vertico, or none)
|
||||
(when prot-emacs-completion-ui
|
||||
|
|
|
|||
|
|
@ -1,147 +1,131 @@
|
|||
;;; Dired file manager and prot-dired.el extras
|
||||
(use-package dired
|
||||
:ensure nil
|
||||
:commands (dired)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq dired-recursive-copies 'always)
|
||||
(setq dired-recursive-deletes 'always)
|
||||
(setq delete-by-moving-to-trash t))
|
||||
|
||||
(use-package dired
|
||||
:ensure nil
|
||||
:commands (dired)
|
||||
:config
|
||||
(setq dired-listing-switches
|
||||
"-AGFhlv --group-directories-first --time-style=long-iso"))
|
||||
(setq dired-listing-switches "-AGFhlv --group-directories-first --time-style=long-iso")
|
||||
|
||||
(use-package dired
|
||||
:ensure nil
|
||||
:commands (dired)
|
||||
:config
|
||||
(setq dired-dwim-target t))
|
||||
(setq dired-dwim-target t)
|
||||
|
||||
(use-package dired
|
||||
:ensure nil
|
||||
:commands (dired)
|
||||
:config
|
||||
(setq dired-guess-shell-alist-user ; those are the suggestions for ! and & in Dired
|
||||
'(("\\.\\(png\\|jpe?g\\|tiff\\)" "feh" "xdg-open")
|
||||
("\\.\\(mp[34]\\|m4a\\|ogg\\|flac\\|webm\\|mkv\\)" "mpv" "xdg-open")
|
||||
(".*" "xdg-open"))))
|
||||
(setq dired-guess-shell-alist-user ; those are the suggestions for ! and & in Dired
|
||||
'(("\\.\\(png\\|jpe?g\\|tiff\\)" "feh *" "feh" "xdg-open")
|
||||
("\\.\\(mp[34]\\|m4a\\|ogg\\|flac\\|webm\\|mkv\\)" "mpv *" "mpv" "xdg-open")
|
||||
(".*" "xdg-open")))
|
||||
|
||||
(use-package dired
|
||||
:ensure nil
|
||||
:commands (dired)
|
||||
:config
|
||||
(setq dired-auto-revert-buffer #'dired-directory-changed-p) ; also see `dired-do-revert-buffer'
|
||||
(setq dired-make-directory-clickable t) ; Emacs 29.1
|
||||
(setq dired-free-space nil) ; Emacs 29.1
|
||||
(setq dired-mouse-drag-files t) ; Emacs 29.1
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'dired
|
||||
(setq dired-auto-revert-buffer #'dired-directory-changed-p) ; also see `dired-do-revert-buffer'
|
||||
(setq dired-make-directory-clickable t) ; Emacs 29.1
|
||||
(setq dired-free-space nil) ; Emacs 29.1
|
||||
(setq dired-mouse-drag-files t) ; Emacs 29.1
|
||||
|
||||
(add-hook 'dired-mode-hook #'dired-hide-details-mode)
|
||||
(add-hook 'dired-mode-hook #'hl-line-mode)
|
||||
(prot-emacs-hook dired-mode-hook (dired-hide-details-mode hl-line-mode))
|
||||
|
||||
;; In Emacs 29 there is a binding for `repeat-mode' which lets you
|
||||
;; repeat C-x C-j just by following it up with j. For me, this is a
|
||||
;; problem as j calls `dired-goto-file', which I often use.
|
||||
(define-key dired-jump-map (kbd "j") nil))
|
||||
;; In Emacs 29 there is a binding for `repeat-mode' which lets you
|
||||
;; repeat C-x C-j just by following it up with j. For me, this is a
|
||||
;; problem as j calls `dired-goto-file', which I often use.
|
||||
(define-key dired-jump-map (kbd "j") nil)))
|
||||
|
||||
(use-package dired-aux
|
||||
:ensure nil
|
||||
:after dired
|
||||
:bind
|
||||
( :map dired-mode-map
|
||||
("C-+" . dired-create-empty-file)
|
||||
("M-s f" . nil)
|
||||
("C-<return>" . dired-do-open) ; Emacs 30
|
||||
("C-x v v" . dired-vc-next-action)) ; Emacs 28
|
||||
:config
|
||||
(setq dired-isearch-filenames 'dwim)
|
||||
(setq dired-create-destination-dirs 'ask) ; Emacs 27
|
||||
(setq dired-vc-rename-file t) ; Emacs 27
|
||||
(setq dired-do-revert-buffer (lambda (dir) (not (file-remote-p dir)))) ; Emacs 28
|
||||
(setq dired-create-destination-dirs-on-trailing-dirsep t)) ; Emacs 29
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'dired
|
||||
(require 'dired-aux)
|
||||
|
||||
(use-package dired-x
|
||||
:ensure nil
|
||||
:after dired
|
||||
:bind
|
||||
( :map dired-mode-map
|
||||
("I" . dired-info))
|
||||
:config
|
||||
(setq dired-clean-up-buffers-too t)
|
||||
(setq dired-clean-confirm-killing-deleted-buffers t)
|
||||
(setq dired-x-hands-off-my-keys t) ; easier to show the keys I use
|
||||
(setq dired-bind-man nil)
|
||||
(setq dired-bind-info nil))
|
||||
(prot-emacs-keybind dired-mode-map
|
||||
"C-+" #'dired-create-empty-file
|
||||
"M-s f" #'nil
|
||||
"C-<return>" #'dired-do-open ; Emacs 30
|
||||
"C-x v v" #'dired-vc-next-action) ; Emacs 28
|
||||
|
||||
(use-package prot-dired
|
||||
:ensure nil
|
||||
:hook (dired-mode . prot-dired-setup-imenu)
|
||||
:bind
|
||||
( :map dired-mode-map
|
||||
("i" . prot-dired-insert-subdir) ; override `dired-maybe-insert-subdir'
|
||||
("/" . prot-dired-limit-regexp)
|
||||
("C-c C-l" . prot-dired-limit-regexp)
|
||||
("M-n" . prot-dired-subdirectory-next)
|
||||
("C-c C-n" . prot-dired-subdirectory-next)
|
||||
("C-c C-p" . prot-dired-subdirectory-previous)
|
||||
("M-s G" . prot-dired-grep-marked-files) ; M-s g is `prot-search-grep'
|
||||
("M-p" . prot-dired-subdirectory-previous)))
|
||||
(setq dired-isearch-filenames 'dwim)
|
||||
(setq dired-create-destination-dirs 'ask) ; Emacs 27
|
||||
(setq dired-vc-rename-file t) ; Emacs 27
|
||||
(setq dired-do-revert-buffer (lambda (dir) (not (file-remote-p dir)))) ; Emacs 28
|
||||
(setq dired-create-destination-dirs-on-trailing-dirsep t) ; Emacs 29
|
||||
|
||||
(use-package dired-subtree
|
||||
:ensure t
|
||||
:bind
|
||||
( :map dired-mode-map
|
||||
("<tab>" . dired-subtree-toggle)
|
||||
("<backtab>" . dired-subtree-remove)) ; S-TAB
|
||||
:config
|
||||
(setq dired-subtree-use-backgrounds nil))
|
||||
(require 'dired-x)
|
||||
|
||||
(use-package wdired
|
||||
:ensure nil
|
||||
:commands (wdired-change-to-wdired-mode)
|
||||
:config
|
||||
(define-key dired-mode-map (kbd "I") #'dired-info)
|
||||
|
||||
(setq dired-clean-up-buffers-too t)
|
||||
(setq dired-clean-confirm-killing-deleted-buffers t)
|
||||
(setq dired-x-hands-off-my-keys t) ; easier to show the keys I use
|
||||
(setq dired-bind-man nil)
|
||||
(setq dired-bind-info nil)))
|
||||
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'dired
|
||||
(require 'prot-dired)
|
||||
(add-hook 'dired-mode #'prot-dired-setup-imenu)
|
||||
(prot-emacs-keybind dired-mode-map
|
||||
"f" nil ; disable `dired-find-file' as it is on RET already
|
||||
"i" #'prot-dired-insert-subdir ; override `dired-maybe-insert-subdir'
|
||||
"/" #'prot-dired-limit-regexp
|
||||
"C-M-<return>" #'prot-dired-open-directory-externally ; C-<return> is `dired-do-open'
|
||||
"C-c C-l" #'prot-dired-limit-regexp
|
||||
"M-n" #'prot-dired-subdirectory-next
|
||||
"C-c C-s" #'prot-dired-search-flat-list
|
||||
"C-c C-r" #'prot-dired-search-flat-list-since-days
|
||||
"C-c C-n" #'prot-dired-subdirectory-next
|
||||
"C-c C-p" #'prot-dired-subdirectory-previous
|
||||
"M-s G" #'prot-dired-grep-marked-files ; M-s g is `prot-search-grep'
|
||||
"M-p" #'prot-dired-subdirectory-previous)))
|
||||
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install dired-subtree)
|
||||
(with-eval-after-load 'dired
|
||||
(prot-emacs-keybind dired-mode-map
|
||||
"<tab>" #'dired-subtree-toggle
|
||||
"TAB" #'dired-subtree-toggle
|
||||
"<backtab>" #'dired-subtree-remove
|
||||
"S-TAB" #'dired-subtree-remove)
|
||||
(setq dired-subtree-use-backgrounds nil)))
|
||||
|
||||
(prot-emacs-configure
|
||||
(setq wdired-allow-to-change-permissions t)
|
||||
(setq wdired-create-parent-directories t))
|
||||
|
||||
(use-package image-dired
|
||||
:ensure nil
|
||||
:commands (image-dired)
|
||||
:bind
|
||||
( :map image-dired-thumbnail-mode-map
|
||||
("<return>" . image-dired-thumbnail-display-external))
|
||||
:config
|
||||
(setq image-dired-thumbnail-storage 'standard)
|
||||
(setq image-dired-external-viewer "xdg-open")
|
||||
(setq image-dired-thumb-size 80)
|
||||
(setq image-dired-thumb-margin 2)
|
||||
(setq image-dired-thumb-relief 0)
|
||||
(setq image-dired-thumbs-per-row 4))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'image-dired
|
||||
(define-key image-dired-thumbnail-mode-map (kbd "<return>") #'image-dired-thumbnail-display-external)
|
||||
(setq image-dired-thumbnail-storage 'standard)
|
||||
(setq image-dired-external-viewer "xdg-open")
|
||||
(setq image-dired-thumb-size 80)
|
||||
(setq image-dired-thumb-margin 2)
|
||||
(setq image-dired-thumb-relief 0)
|
||||
(setq image-dired-thumbs-per-row 4)))
|
||||
|
||||
;;; Automatically preview Dired file at point (dired-preview.el)
|
||||
;; One of my packages: <https://protesilaos.com/emacs>
|
||||
(use-package dired-preview
|
||||
:ensure t
|
||||
:hook (dired-mode . (lambda ()
|
||||
(when (string-match-p "Pictures" default-directory)
|
||||
(dired-preview-mode 1))))
|
||||
:config
|
||||
;; These are all set to their default values. I keep them here for
|
||||
;; reference.
|
||||
(setq dired-preview-max-size (* (expt 2 20) 6))
|
||||
(setq dired-preview-delay 0.3)
|
||||
(setq dired-preview-ignored-extensions-regexp
|
||||
(concat "\\."
|
||||
"\\(mkv\\|" "webm\\|" "mp4\\|" "mp3\\|" "ogg\\|" "m4a\\|"
|
||||
"gz\\|" "zst\\|" "tar\\|" "xz\\|" "rar\\|" "zip\\|"
|
||||
"iso\\|" "epub\\|" "\\)")))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install dired-preview)
|
||||
(with-eval-after-load 'dired
|
||||
(require 'dired-preview)
|
||||
(add-hook 'dired-mode-hook (lambda ()
|
||||
(when (string-match-p "Pictures" default-directory)
|
||||
(dired-preview-mode 1))))
|
||||
(define-key dired-mode-map (kbd "V") #'dired-preview-mode)
|
||||
|
||||
(setq dired-preview-trigger-on-start nil)
|
||||
(setq dired-preview-max-size (* (expt 2 20) 10))
|
||||
(setq dired-preview-delay 0.5)
|
||||
(setq dired-preview-ignored-extensions-regexp
|
||||
(concat "\\."
|
||||
"\\(gz\\|"
|
||||
"zst\\|"
|
||||
"tar\\|"
|
||||
"xz\\|"
|
||||
"rar\\|"
|
||||
"zip\\|"
|
||||
"iso\\|"
|
||||
"epub"
|
||||
"\\)"))
|
||||
|
||||
(setq dired-preview-display-action-alist #'dired-preview-display-action-alist-below)))
|
||||
|
||||
;;; dired-like mode for the trash (trashed.el)
|
||||
(use-package trashed
|
||||
:ensure t
|
||||
:commands (trashed)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install trashed)
|
||||
(setq trashed-action-confirmer 'y-or-n-p)
|
||||
(setq trashed-use-header-line t)
|
||||
(setq trashed-sort-key '("Date deleted" . t))
|
||||
|
|
@ -149,17 +133,18 @@
|
|||
|
||||
;;; Play back media with Dired (mandoura.el)
|
||||
;; This is yet another package of mine: <https://protesilaos.com/emacs>
|
||||
(use-package mandoura
|
||||
;; The :vc keyword is part of Emacs 30. Read the manual for what keywords it reads: (info "(emacs) Fetching Package Sources")
|
||||
:vc ( :url "https://github.com/protesilaos/mandoura")
|
||||
:bind
|
||||
( :map global-map
|
||||
("M-<AudioPlay>" . mandoura-return-track-title-and-time)
|
||||
("M-<XF86AudioPlay>" . mandoura-return-track-title-and-time)
|
||||
:map dired-mode-map
|
||||
("M-<return>" . mandoura-play-files)
|
||||
("M-RET" . mandoura-play-files))
|
||||
:config
|
||||
(setq mandoura-saved-playlist-directory "~/Music/playlists/"))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install-vc
|
||||
mandoura
|
||||
"https://github.com/protesilaos/mandoura.git")
|
||||
(with-eval-after-load 'dired
|
||||
(require 'mandoura)
|
||||
(prot-emacs-keybind global-map
|
||||
"M-<AudioPlay>" #'mandoura-return-track-title-and-time
|
||||
"M-<XF86AudioPlay>" #'mandoura-return-track-title-and-time)
|
||||
(prot-emacs-keybind dired-mode-map
|
||||
"M-<return>" #'mandoura-play-playlist
|
||||
"M-RET" #'mandoura-play-playlist)
|
||||
(setq mandoura-saved-playlist-directory "~/Music/playlists/")))
|
||||
|
||||
(provide 'prot-emacs-dired)
|
||||
|
|
|
|||
32
emacs/.emacs.d/prot-emacs-modules/prot-emacs-doric-themes.el
Normal file
32
emacs/.emacs.d/prot-emacs-modules/prot-emacs-doric-themes.el
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
;;; The Doric themes
|
||||
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install doric-themes)
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'doric-themes-rotate
|
||||
"C-<f5>" #'doric-themes-select)
|
||||
|
||||
(doric-themes-load-random
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
'dark
|
||||
'light)))
|
||||
|
||||
;; For testing purposes
|
||||
(prot-emacs-comment
|
||||
(:eval nil)
|
||||
|
||||
(add-to-list 'load-path "~/Git/Projects/doric-themes/")
|
||||
|
||||
(require 'doric-themes)
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'doric-themes-rotate
|
||||
"C-<f5>" #'doric-themes-select)
|
||||
|
||||
(doric-themes-load-random
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
'dark
|
||||
'light)))
|
||||
|
||||
(provide 'prot-emacs-doric-themes)
|
||||
|
|
@ -1,15 +1,21 @@
|
|||
;;; The Ef (εὖ) themes
|
||||
|
||||
;; The themes are customisable. Read the manual:
|
||||
;; <https://protesilaos.com/emacs/ef-themes>.
|
||||
(use-package ef-themes
|
||||
:ensure t
|
||||
:demand t
|
||||
:bind ("<f5>" . ef-themes-select)
|
||||
:config
|
||||
(setq ef-themes-variable-pitch-ui t
|
||||
ef-themes-mixed-fonts t
|
||||
ef-themes-headings ; read the manual's entry of the doc string
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install ef-themes)
|
||||
|
||||
(ef-themes-take-over-modus-themes-mode 1)
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'modus-themes-rotate
|
||||
"C-<f5>" #'modus-themes-select
|
||||
"M-<f5>" #'modus-themes-load-random)
|
||||
|
||||
(setq modus-themes-variable-pitch-ui t
|
||||
modus-themes-mixed-fonts t
|
||||
modus-themes-bold-constructs t
|
||||
modus-themes-italic-constructs t
|
||||
modus-themes-to-rotate nil ; defaults to the return value of `modus-themes-get-themes'
|
||||
modus-themes-headings ; read the manual's entry of the doc string
|
||||
'((0 . (variable-pitch light 1.9))
|
||||
(1 . (variable-pitch light 1.8))
|
||||
(2 . (variable-pitch regular 1.7))
|
||||
|
|
@ -22,78 +28,28 @@
|
|||
(agenda-structure . (variable-pitch light 1.9))
|
||||
(t . (variable-pitch 1.1))))
|
||||
|
||||
;; The `ef-themes' provide lots of themes. I want to pick one at
|
||||
;; random when I start Emacs: the `ef-themes-load-random' does just
|
||||
;; that (it can be called interactively as well). I just check with
|
||||
;; my desktop environment to determine if the choice should be about
|
||||
;; a light or a dark theme. Those functions are in my init.el.
|
||||
(if (prot-emacs-theme-environment-dark-p)
|
||||
(ef-themes-load-random 'dark)
|
||||
(ef-themes-load-random 'light)))
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
(modus-themes-load-random 'dark)
|
||||
(modus-themes-load-random 'light)))
|
||||
|
||||
;; NOTE: For testing purposes
|
||||
(prot-emacs-comment
|
||||
(:eval nil)
|
||||
(progn
|
||||
(mapc #'disable-theme custom-enabled-themes)
|
||||
|
||||
(add-to-list 'load-path "/home/prot/Git/Projects/ef-themes/")
|
||||
(add-to-list 'load-path "~/Git/Projects/modus-themes/")
|
||||
(add-to-list 'load-path "~/Git/Projects/ef-themes/")
|
||||
|
||||
(require 'ef-themes)
|
||||
(load-theme 'ef-arbutus t t)
|
||||
(load-theme 'ef-autumn t t)
|
||||
(load-theme 'ef-bio t t)
|
||||
(load-theme 'ef-cherie t t)
|
||||
(load-theme 'ef-cyprus t t)
|
||||
(load-theme 'ef-dark t t)
|
||||
(load-theme 'ef-day t t)
|
||||
(load-theme 'ef-deuteranopia-dark t t)
|
||||
(load-theme 'ef-deuteranopia-light t t)
|
||||
(load-theme 'ef-duo-dark t t)
|
||||
(load-theme 'ef-duo-light t t)
|
||||
(load-theme 'ef-frost t t)
|
||||
(load-theme 'ef-kassio t t)
|
||||
(load-theme 'ef-light t t)
|
||||
(load-theme 'ef-melissa-dark t t)
|
||||
(load-theme 'ef-melissa-light t t)
|
||||
(load-theme 'ef-night t t)
|
||||
(load-theme 'ef-rosa t t)
|
||||
(load-theme 'ef-spring t t)
|
||||
(load-theme 'ef-summer t t)
|
||||
(load-theme 'ef-symbiosis t t)
|
||||
(load-theme 'ef-trio-dark t t)
|
||||
(load-theme 'ef-trio-light t t)
|
||||
(load-theme 'ef-tritanopia-dark t t)
|
||||
(load-theme 'ef-tritanopia-light t t)
|
||||
(load-theme 'ef-winter t t)
|
||||
(require 'modus-themes)
|
||||
(require 'ef-themes)
|
||||
|
||||
(setq ef-themes-headings ; read the manual's entry or the doc string
|
||||
'((0 . (variable-pitch light 1.9))
|
||||
(1 . (variable-pitch light 1.8))
|
||||
(2 . (variable-pitch light 1.7))
|
||||
(3 . (variable-pitch semilight 1.6))
|
||||
(4 . (variable-pitch semilight 1.5))
|
||||
(5 . (variable-pitch regular 1.4))
|
||||
(6 . (variable-pitch regular 1.3))
|
||||
(7 . (variable-pitch 1.2)) ; absence of weight means `bold'
|
||||
(agenda-date . (semilight 1.5))
|
||||
(agenda-structure . (variable-pitch light 1.9))
|
||||
(t . (variable-pitch 1.1))))
|
||||
(ef-themes-take-over-modus-themes-mode 1)
|
||||
|
||||
;; They are nil by default...
|
||||
(setq ef-themes-mixed-fonts t
|
||||
ef-themes-variable-pitch-ui nil)
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'modus-themes-rotate
|
||||
"C-<f5>" #'modus-themes-select
|
||||
"M-<f5>" #'modus-themes-load-random)
|
||||
|
||||
(mapcar (lambda (theme)
|
||||
(add-to-list
|
||||
'custom-theme-load-path
|
||||
(concat "/home/prot/Git/Projects/ef-themes/" (symbol-name theme) "-theme.el")))
|
||||
(ef-themes--list-enabled-themes))
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
(modus-themes-load-random 'dark)
|
||||
(modus-themes-load-random 'light)))
|
||||
|
||||
(if (prot-emacs-theme-environment-dark-p)
|
||||
(ef-themes-load-random 'dark)
|
||||
(ef-themes-load-random 'light))
|
||||
|
||||
(define-key global-map (kbd "<f5>") #'ef-themes-select)))
|
||||
|
||||
(provide 'prot-emacs-ef-themes)
|
||||
(provide 'prot-emacs-ef-themes)
|
||||
|
|
|
|||
|
|
@ -1,44 +1,29 @@
|
|||
;;;; File with authentication credentials (`auth-source')
|
||||
(use-package auth-source
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq auth-sources '("~/.authinfo.gpg")
|
||||
user-full-name "Protesilaos Stavrou"
|
||||
user-mail-address "public@protesilaos.com"))
|
||||
user-full-name "Protesilaos"
|
||||
user-mail-address "prot@protesilaos.com"))
|
||||
|
||||
;;;; Encryption settings (`mm-encode' and `mml-sec')
|
||||
|
||||
(use-package mm-encode
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq mm-encrypt-option nil ; use 'guided for both if you need more control
|
||||
mm-sign-option nil))
|
||||
mm-sign-option nil)
|
||||
|
||||
(use-package mml-sec
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(setq mml-secure-openpgp-encrypt-to-self t
|
||||
mml-secure-openpgp-sign-with-sender t
|
||||
mml-secure-smime-encrypt-to-self t
|
||||
mml-secure-smime-sign-with-sender t))
|
||||
mml-secure-smime-sign-with-sender t)
|
||||
|
||||
;;;; Message composition (`message')
|
||||
|
||||
(use-package message
|
||||
:ensure nil
|
||||
:defer t
|
||||
:hook
|
||||
(message-setup . message-sort-headers)
|
||||
:config
|
||||
(add-hook 'message-setup-hook #'message-sort-headers)
|
||||
|
||||
(setq mail-user-agent 'message-user-agent
|
||||
message-mail-user-agent t) ; use `mail-user-agent'
|
||||
(setq mail-header-separator "--text follows this line--")
|
||||
(setq message-elide-ellipsis "\n> [... %l lines elided]\n")
|
||||
(setq compose-mail-user-agent-warnings nil)
|
||||
(setq message-signature "Protesilaos Stavrou\nhttps://protesilaos.com\n"
|
||||
(setq message-signature "Protesilaos\nhttps://protesilaos.com\n"
|
||||
mail-signature message-signature)
|
||||
(setq message-citation-line-function #'message-insert-formatted-citation-line)
|
||||
(setq message-citation-line-format (concat "> From: %f\n"
|
||||
|
|
@ -48,27 +33,37 @@
|
|||
(setq message-confirm-send nil)
|
||||
(setq message-kill-buffer-on-exit t)
|
||||
;; (add-to-list 'mm-body-charset-encoding-alist '(utf-8 . base64))
|
||||
(setq message-wide-reply-confirm-recipients nil))
|
||||
(setq message-forward-as-mime t)
|
||||
(setq message-wide-reply-confirm-recipients nil)
|
||||
|
||||
(defun prot/message-copy-body ()
|
||||
"Copy the body of the current message buffer."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(let ((beginning (message-goto-body))
|
||||
(end (progn
|
||||
(message-goto-signature)
|
||||
(forward-line -2)
|
||||
(point))))
|
||||
(kill-new (buffer-substring-no-properties beginning end))
|
||||
(message "Copied the body of the current message buffer")
|
||||
(setq this-command 'kill-ring-save))))
|
||||
|
||||
(with-eval-after-load 'message
|
||||
;; Override `message-insert-signature'.
|
||||
(define-key message-mode-map (kbd "C-c C-w") #'prot/message-copy-body)))
|
||||
|
||||
;;;; Add attachments from Dired (`gnus-dired' does not require `gnus')
|
||||
(use-package gnus-dired
|
||||
:ensure nil
|
||||
:after message
|
||||
:hook
|
||||
(dired-mode . turn-on-gnus-dired-mode))
|
||||
(prot-emacs-configure
|
||||
(add-hook 'dired-mode-hook #'turn-on-gnus-dired-mode))
|
||||
|
||||
;;;; `sendmail' (mail transfer agent)
|
||||
(use-package sendmail
|
||||
:ensure nil
|
||||
:after message
|
||||
:config
|
||||
(setq send-mail-function 'sendmail-send-it
|
||||
;; ;; NOTE 2023-08-08: We do not need this if we have the Arch
|
||||
;; ;; Linux `msmtp-mta' package installed: it replaces the
|
||||
;; ;; generic sendmail executable with msmtp.
|
||||
;;
|
||||
;; sendmail-program (executable-find "msmtp")
|
||||
message-sendmail-envelope-from 'header))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'message
|
||||
(setq send-mail-function #'smtpmail-send-it)
|
||||
(setq smtpmail-smtp-server "mail.gandi.net")
|
||||
(setq smtpmail-smtp-service 587)
|
||||
(setq smtpmail-stream-type 'starttls)))
|
||||
|
||||
(when (executable-find "notmuch")
|
||||
(require 'prot-emacs-notmuch))
|
||||
|
|
|
|||
|
|
@ -1,179 +1,239 @@
|
|||
;;; Essential configurations
|
||||
(use-package emacs
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
|
||||
;;;; General settings and common custom functions (prot-simple.el)
|
||||
(setq backward-delete-char-untabify-method nil)
|
||||
(setq blink-matching-paren nil)
|
||||
(setq custom-unlispify-tag-names nil)
|
||||
(setq delete-pair-blink-delay 0.1) ; Emacs28 -- see `prot-simple-delete-pair-dwim'
|
||||
(setq help-window-select t)
|
||||
(setq next-error-recenter '(4)) ; center of the window
|
||||
(setq find-library-include-other-files nil) ; Emacs 29
|
||||
(setq remote-file-name-inhibit-delete-by-moving-to-trash t) ; Emacs 30
|
||||
(setq remote-file-name-inhibit-auto-save t) ; Emacs 30
|
||||
(setq tramp-connection-timeout (* 60 10)) ; seconds
|
||||
(setq save-interprogram-paste-before-kill t)
|
||||
(setq mode-require-final-newline 'visit-save)
|
||||
(setq-default truncate-partial-width-windows nil)
|
||||
(setq eval-expression-print-length nil)
|
||||
(setq kill-do-not-save-duplicates t)
|
||||
(setq duplicate-line-final-position -1 ; both are Emacs 29
|
||||
duplicate-region-final-position -1)
|
||||
(setq scroll-error-top-bottom t)
|
||||
(setq delete-pair-push-mark t) ; Emacs 31
|
||||
(setq echo-keystrokes-help nil) ; Emacs 30
|
||||
(setq epa-keys-select-method 'minibuffer) ; Emacs 30
|
||||
(setq eval-expression-print-length nil)
|
||||
(setq find-library-include-other-files nil) ; Emacs 29
|
||||
(setq help-window-select t)
|
||||
(setq help-window-keep-selected t) ; Emacs 29
|
||||
(setq kill-do-not-save-duplicates t)
|
||||
(setq mode-require-final-newline 'visit-save)
|
||||
(setq next-error-recenter '(4)) ; center of the window
|
||||
(setq remote-file-name-inhibit-auto-save t) ; Emacs 30
|
||||
(setq remote-file-name-inhibit-delete-by-moving-to-trash t) ; Emacs 30
|
||||
(setq save-interprogram-paste-before-kill t)
|
||||
(setq tramp-connection-timeout (* 60 10)) ; seconds
|
||||
(setq trusted-content '("~/Git/Projects/" "~/Coach/")) ; Emacs 30
|
||||
(setq truncate-partial-width-windows nil)
|
||||
|
||||
;; Keys I unbind here are either to avoid accidents or to bind them
|
||||
;; elsewhere later in the configuration.
|
||||
:bind
|
||||
( :map global-map
|
||||
("<insert>" . nil)
|
||||
("<menu>" . nil)
|
||||
("C-z" . nil) ; I have a window manager, thanks!
|
||||
("C-x C-z" . nil) ; same idea as above
|
||||
("C-x C-c" . nil) ; avoid accidentally exiting Emacs
|
||||
("C-x C-c C-c" . save-buffers-kill-emacs) ; more cumbersome, less error-prone
|
||||
("C-h h" . nil) ; Never show that "hello" file
|
||||
("M-`" . nil)
|
||||
("M-o" . delete-blank-lines) ; alias for C-x C-o
|
||||
("M-SPC" . cycle-spacing)
|
||||
("M-z" . zap-up-to-char) ; NOT `zap-to-char'
|
||||
("M-c" . capitalize-dwim)
|
||||
("M-l" . downcase-dwim) ; "lower" case
|
||||
("M-u" . upcase-dwim)
|
||||
("M-=" . count-words)
|
||||
("C-x O" . next-multiframe-window)
|
||||
("C-h K" . describe-keymap) ; overrides `Info-goto-emacs-key-command-node'
|
||||
("C-h u" . apropos-user-option)
|
||||
("C-h F" . apropos-function) ; lower case is `describe-function'
|
||||
("C-h V" . apropos-variable) ; lower case is `describe-variable'
|
||||
("C-h L" . apropos-library) ; lower case is `view-lossage'
|
||||
("C-h c" . describe-char) ; overrides `describe-key-briefly'
|
||||
|
||||
:map prog-mode-map
|
||||
("C-M-d" . up-list) ; confusing name for what looks like "down" to me
|
||||
("<C-M-backspace>" . backward-kill-sexp)
|
||||
(prot-emacs-keybind global-map
|
||||
"<f2>" #'toggle-input-method ; F2 overrides that two-column gimmick. Sorry, but no!
|
||||
"<insert>" nil
|
||||
"<menu>" nil
|
||||
"C-M-w" nil ; never use it
|
||||
"C-x C-d" nil ; never use it
|
||||
"C-x C-v" nil ; never use it
|
||||
"C-z" nil ; I have a window manager, thanks!
|
||||
"C-x C-z" nil ; same idea as above
|
||||
"C-x C-c" nil ; avoid accidentally exiting Emacs
|
||||
"C-x C-c C-c" #'save-buffers-kill-emacs ; more cumbersome, less error-prone
|
||||
"C-h h" nil ; Never show that "hello" file
|
||||
"M-`" nil
|
||||
"M-o" #'delete-blank-lines ; alias for C-x C-o
|
||||
"M-SPC" #'cycle-spacing
|
||||
"M-z" #'zap-up-to-char ; NOT `zap-to-char'
|
||||
"M-c" #'capitalize-dwim
|
||||
"M-l" #'downcase-dwim ; "lower" case
|
||||
"M-u" #'upcase-dwim
|
||||
"M-=" #'count-words
|
||||
"C-x O" #'next-multiframe-window
|
||||
"C-h K" #'describe-keymap ; overrides `Info-goto-emacs-key-command-node'
|
||||
"C-h u" #'apropos-user-option
|
||||
"C-h F" #'apropos-function ; lower case is `describe-function'
|
||||
"C-h V" #'apropos-variable ; lower case is `describe-variable'
|
||||
"C-h L" #'apropos-library ; lower case is `view-lossage'
|
||||
"C-h c" #'describe-char) ; overrides `describe-key-briefly'
|
||||
|
||||
;; Keymap for buffers (Emacs28)
|
||||
:map ctl-x-x-map
|
||||
("f" . follow-mode) ; override `font-lock-update'
|
||||
("r" . rename-uniquely)
|
||||
("l" . visual-line-mode)))
|
||||
(prot-emacs-keybind prog-mode-map
|
||||
"C-M-d" #'up-list ; confusing name for what looks like "down" to me
|
||||
"<C-M-backspace>" #'backward-kill-sexp)
|
||||
|
||||
;; Keymap for buffers (Emacs28)
|
||||
(prot-emacs-keybind ctl-x-x-map
|
||||
"f" #'follow-mode ; override `font-lock-update'
|
||||
"r" #'rename-uniquely
|
||||
"l" #'visual-line-mode))
|
||||
|
||||
(prot-emacs-configure
|
||||
(require 'prot-common)
|
||||
|
||||
(defvar prot/fundamental-mode-hook nil
|
||||
"Normal hook for `fundamental-mode' (which is missing by default).")
|
||||
|
||||
(defun prot/fundamental-mode-run-hook (&rest args)
|
||||
"Apply ARGS and then run `prot/fundamental-mode-hook'."
|
||||
(apply args)
|
||||
(run-hooks 'prot/fundamental-mode-hook))
|
||||
|
||||
(advice-add #'fundamental-mode :around #'prot/fundamental-mode-run-hook)
|
||||
|
||||
(prot-emacs-hook
|
||||
(text-mode-hook prog-mode-hook dired-mode-hook prot/fundamental-mode-hook hexl-mode-hook comint-mode-hook)
|
||||
prot-common-truncate-lines-silently)
|
||||
|
||||
(use-package prot-common
|
||||
:ensure nil
|
||||
:functions (prot-common-truncate-lines-silently)
|
||||
:hook ((fundamental-mode text-mode prog-mode) . prot-common-truncate-lines-silently)
|
||||
:config
|
||||
;; NEVER tell me which key can call a command that I specifically
|
||||
;; invoked with M-x: I have a good reason to use it that way.
|
||||
(advice-add #'execute-extended-command--describe-binding-msg :override #'prot-common-ignore))
|
||||
|
||||
(use-package prot-simple
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(require 'prot-simple)
|
||||
|
||||
(setq prot-simple-date-specifier "%F")
|
||||
(setq prot-simple-time-specifier "%R %z")
|
||||
|
||||
(advice-add #'save-buffers-kill-emacs :before #'prot-simple-display-unsaved-buffers-on-exit)
|
||||
|
||||
(with-eval-after-load 'pulsar
|
||||
(add-hook 'prot-simple-file-to-register-jump-hook #'pulsar-recenter-center)
|
||||
(add-hook 'prot-simple-file-to-register-jump-hook #'pulsar-reveal-entry))
|
||||
:bind
|
||||
( ("ESC ESC" . prot-simple-keyboard-quit-dwim)
|
||||
("C-g" . prot-simple-keyboard-quit-dwim)
|
||||
("C-M-SPC" . prot-simple-mark-sexp) ; will be overriden by `expreg' if tree-sitter is available
|
||||
;; All `prot-simple-override-mode' does is activate a keymap.
|
||||
;; Below I add keys to that map. Because the mode is enabled
|
||||
;; globally, those keys take precedence over the ones specified by
|
||||
;; any given major mode. In principle, this means that my keys will
|
||||
;; always work (though technically they can be overriden by another
|
||||
;; minor mode, depending on which one is evaluated last).
|
||||
(prot-simple-override-mode 1)
|
||||
|
||||
(defun prot/simple-hex-highlight ()
|
||||
(when-let* ((file buffer-file-name)
|
||||
((derived-mode-p 'emacs-lisp-mode))
|
||||
((string-match-p "-theme" file)))
|
||||
(prot-simple-hex-color-mode 1)))
|
||||
|
||||
(add-hook 'emacs-lisp-mode-hook #'prot/simple-hex-highlight)
|
||||
|
||||
(define-key ctl-x-x-map (kbd "c") #'prot-simple-hex-color-mode) ; C-x x c
|
||||
|
||||
(prot-emacs-keybind prot-simple-override-mode-map
|
||||
"C-a" #'prot-simple-duplicate-line-or-region ; "again" mnemonic, overrides `move-beginning-of-line'
|
||||
"C-d" #'prot-simple-delete-line ; overrides `delete-char'
|
||||
"C-v" #'prot-simple-multi-line-below ; overrides `scroll-up-command'
|
||||
"<next>" #'prot-simple-multi-line-below ; overrides `scroll-up-command'
|
||||
"M-v" #'prot-simple-multi-line-above ; overrides `scroll-down-command'
|
||||
"<prior>" #'prot-simple-multi-line-above ; overrides `scroll-down-command'
|
||||
"C-M-i" #'prot-simple-indent-dwim ; overrides `completion-at-point'
|
||||
"C-M-\\" #'prot-simple-indent-dwim ; overrides `indent-region'
|
||||
"C-M-c" #'completion-at-point) ; overrides `exit-recursive-edit'
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"C-h h" #'prot-simple-describe-at-point
|
||||
"<escape>" #'prot-simple-keyboard-quit-dwim
|
||||
"C-g" #'prot-simple-keyboard-quit-dwim
|
||||
"C-M-SPC" #'prot-simple-mark-sexp
|
||||
"C-x 0" #'prot-simple-delete-window-dwim ; overrides `delete-window'
|
||||
;; Commands for lines
|
||||
("M-k" . prot-simple-kill-line-backward)
|
||||
("C-S-d" . prot-simple-duplicate-line-or-region)
|
||||
("C-S-w" . prot-simple-copy-line)
|
||||
("C-S-y" . prot-simple-yank-replace-line-or-region)
|
||||
("C-v" . prot-simple-multi-line-below) ; overrides `scroll-up-command'
|
||||
("<next>" . prot-simple-multi-line-below) ; overrides `scroll-up-command'
|
||||
("M-v" . prot-simple-multi-line-above) ; overrides `scroll-down-command'
|
||||
("<prior>" . prot-simple-multi-line-above) ; overrides `scroll-down-command'
|
||||
("<C-return>" . prot-simple-new-line-below)
|
||||
("<C-S-return>" . prot-simple-new-line-above)
|
||||
"C-S-d" #'prot-simple-delete-line-backward
|
||||
"C-S-k" #'prot-simple-kill-line-backward
|
||||
"M-k" #'prot-simple-copy-line-forward
|
||||
"M-K" #'prot-simple-copy-line-backward
|
||||
"M-j" #'delete-indentation
|
||||
"C-w" #'prot-simple-kill-region
|
||||
"M-w" #'prot-simple-kill-ring-save
|
||||
"C-S-w" #'prot-simple-copy-line
|
||||
"C-S-y" #'prot-simple-yank-replace-line-or-region
|
||||
"<C-return>" #'prot-simple-new-line-below
|
||||
"<C-S-return>" #'prot-simple-new-line-above
|
||||
"C-x x a" #'prot-simple-auto-fill-visual-line-mode ; auto-fill/visual-line toggle
|
||||
;; Commands for text insertion or manipulation
|
||||
("C-=" . prot-simple-insert-date)
|
||||
("C-<" . prot-simple-escape-url-dwim)
|
||||
;; "C->" prot-simple-insert-line-prefix-dwim
|
||||
("M-Z" . prot-simple-zap-to-char-backward)
|
||||
"C-=" #'prot-simple-insert-date
|
||||
"C-<" #'prot-simple-escape-url-dwim
|
||||
"C->" #'prot-simple-escape-url-dwim
|
||||
"M-Z" #'prot-simple-zap-to-char-backward
|
||||
;; Commands for object transposition
|
||||
("C-S-p" . prot-simple-move-above-dwim)
|
||||
("C-S-n" . prot-simple-move-below-dwim)
|
||||
("C-t" . prot-simple-transpose-chars)
|
||||
("C-x C-t" . prot-simple-transpose-lines)
|
||||
("C-S-t" . prot-simple-transpose-paragraphs)
|
||||
("C-x M-t" . prot-simple-transpose-sentences)
|
||||
("C-M-t" . prot-simple-transpose-sexps)
|
||||
("M-t" . prot-simple-transpose-words)
|
||||
"C-S-p" #'prot-simple-move-above-dwim
|
||||
"C-S-n" #'prot-simple-move-below-dwim
|
||||
"C-t" #'prot-simple-transpose-chars
|
||||
"C-x C-t" #'prot-simple-transpose-lines
|
||||
"C-S-t" #'prot-simple-transpose-paragraphs
|
||||
"C-x M-t" #'prot-simple-transpose-sentences
|
||||
"C-M-t" #'prot-simple-transpose-sexps
|
||||
"M-t" #'prot-simple-transpose-words
|
||||
;; Commands for paragraphs
|
||||
("M-Q" . prot-simple-unfill-region-or-paragraph)
|
||||
"M-Q" #'prot-simple-unfill-region-or-paragraph
|
||||
;; Commands for windows and pages
|
||||
("C-x o" . prot-simple-other-window)
|
||||
("C-x n k" . prot-simple-delete-page-delimiters)
|
||||
("C-x M-r" . prot-simple-swap-window-buffers)
|
||||
"C-x o" #'prot-simple-other-window
|
||||
"C-x n k" #'prot-simple-delete-page-delimiters
|
||||
"M-r" #'window-layout-transpose ; Emacs 31 override `move-to-window-line-top-bottom'
|
||||
"M-S-r" #'rotate-windows-back ; Emacs 31
|
||||
;; Commands for buffers
|
||||
("<C-f2>" . prot-simple-rename-file-and-buffer)
|
||||
("C-x k" . prot-simple-kill-buffer-current)
|
||||
("C-x K" . kill-buffer) ; leaving this here to contrast with the above
|
||||
("M-s b" . prot-simple-buffers-major-mode)
|
||||
("M-s v" . prot-simple-buffers-vc-root)
|
||||
;; Commands for files
|
||||
("C-x r ." . prot-simple-file-to-register)))
|
||||
"C-<f2>" #'prot-simple-rename-file-and-buffer
|
||||
"M-<f2>" #'prot-simple-copy-current-path
|
||||
"C-x k" #'prot-simple-kill-buffer-dwim
|
||||
"C-x K" #'kill-buffer ; leaving this here to contrast with the above
|
||||
"M-s b" #'prot-simple-buffers-major-mode
|
||||
"M-s v" #'prot-simple-buffers-vc-root))
|
||||
|
||||
;;;; Scratch buffers per major mode (prot-scratch.el)
|
||||
(use-package prot-scratch
|
||||
:ensure nil
|
||||
:bind ("C-c s" . prot-scratch-buffer)
|
||||
:config
|
||||
(setq prot-scratch-default-mode 'text-mode))
|
||||
(prot-emacs-configure
|
||||
(setq prot-scratch-default-mode 'text-mode)
|
||||
(autoload #'prot-scratch-buffer "prot-scratch")
|
||||
(define-key global-map (kbd "C-c s") #'prot-scratch-buffer))
|
||||
|
||||
;;;; Insert character pairs (prot-pair.el)
|
||||
(use-package prot-pair
|
||||
:ensure nil
|
||||
:bind
|
||||
(("C-'" . prot-pair-insert)
|
||||
("M-'" . prot-pair-insert)
|
||||
("M-\\" . prot-pair-delete)))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-autoload
|
||||
(prot-pair-insert prot-pair-insert-directly prot-pair-delete)
|
||||
"prot-pair")
|
||||
(prot-emacs-keybind global-map
|
||||
"C-'" #'prot-pair-insert
|
||||
"M-'" #'prot-pair-insert-directly
|
||||
"M-\\" #'prot-pair-delete))
|
||||
|
||||
;;;; Comments (prot-comment.el)
|
||||
(use-package prot-comment
|
||||
:ensure nil
|
||||
:init
|
||||
(prot-emacs-configure
|
||||
(setq comment-empty-lines t)
|
||||
(setq comment-fill-column nil)
|
||||
(setq comment-multi-line t)
|
||||
(setq comment-style 'multi-line)
|
||||
(setq-default comment-column 0)
|
||||
|
||||
(setq prot-comment-comment-keywords '("TODO" "NOTE" "XXX" "REVIEW" "FIXME"))
|
||||
(setq prot-comment-comment-keywords '("TODO" "NOTE" "FIXME"))
|
||||
(setq prot-comment-timestamp-format-concise "%F")
|
||||
(setq prot-comment-timestamp-format-verbose "%F %T %z")
|
||||
:bind
|
||||
(("C-;" . prot-comment)
|
||||
("C-x C-;" . prot-comment-timestamp-keyword)))
|
||||
|
||||
(prot-emacs-autoload
|
||||
(prot-comment prot-comment-timestamp-keyword)
|
||||
"prot-comment")
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"C-;" #'prot-comment
|
||||
"M-;" #'prot-comment ; overrides `comment-dwim'
|
||||
"C-x C-;" #'prot-comment-timestamp-keyword))
|
||||
|
||||
;;;; Prefix keymap (prot-prefix.el)
|
||||
(use-package prot-prefix
|
||||
:ensure nil
|
||||
:bind-keymap
|
||||
;; F2 overrides that two-column gimmick. Sorry, but no.
|
||||
(("<insert>" . prot-prefix)
|
||||
("<f2>" . prot-prefix)
|
||||
("C-z" . prot-prefix)))
|
||||
(prot-emacs-configure
|
||||
(require 'prot-prefix)
|
||||
(prot-emacs-keybind global-map
|
||||
"<insert>" #'prot-prefix
|
||||
"C-z" #'prot-prefix))
|
||||
|
||||
(prot-emacs-configure
|
||||
(define-key global-map (kbd "C-x C-r") #'recentf-open) ; override `find-file-read-only'
|
||||
|
||||
(with-eval-after-load 'recentf
|
||||
(setq recentf-max-saved-items 100)
|
||||
(setq recentf-max-menu-items 25) ; I don't use the `menu-bar-mode', but this is good to know
|
||||
(setq recentf-save-file-modes nil)
|
||||
(setq recentf-keep nil)
|
||||
(setq recentf-auto-cleanup nil)
|
||||
(setq recentf-initialize-file-name-history nil)
|
||||
(setq recentf-filename-handlers nil)
|
||||
(setq recentf-show-file-shortcuts-flag nil)
|
||||
(recentf-mode 1)))
|
||||
|
||||
;;;; Mouse and mouse wheel behaviour
|
||||
(use-package mouse
|
||||
:ensure nil
|
||||
:hook (after-init . mouse-wheel-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(mouse-wheel-mode 1)
|
||||
;; Some of these variables are defined in places other than
|
||||
;; mouse.el, but this is fine.
|
||||
(setq mouse-autoselect-window t) ; complements the auto-selection of my tiling window manager
|
||||
(setq focus-follows-mouse t)
|
||||
|
||||
;; In Emacs 27+, use Control + mouse wheel to scale text.
|
||||
(setq mouse-wheel-scroll-amount
|
||||
|
|
@ -187,16 +247,16 @@
|
|||
mouse-wheel-follow-mouse t)
|
||||
|
||||
;; Scrolling behaviour
|
||||
(setq-default scroll-preserve-screen-position t
|
||||
scroll-conservatively 1 ; affects `scroll-step'
|
||||
scroll-margin 0
|
||||
next-screen-context-lines 0))
|
||||
(setq scroll-error-top-bottom t
|
||||
scroll-preserve-screen-position t
|
||||
scroll-conservatively 1 ; affects `scroll-step'
|
||||
scroll-margin 0
|
||||
next-screen-context-lines 0))
|
||||
|
||||
;;;; Repeatable key chords (repeat-mode)
|
||||
(use-package repeat
|
||||
:ensure nil
|
||||
:hook (after-init . repeat-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(repeat-mode 1)
|
||||
|
||||
(setq repeat-on-final-keystroke t
|
||||
repeat-exit-timeout 5
|
||||
repeat-exit-key "<escape>"
|
||||
|
|
@ -208,11 +268,8 @@
|
|||
set-mark-command-repeat-pop t))
|
||||
|
||||
;;;; Built-in bookmarking framework (bookmark.el)
|
||||
(use-package bookmark
|
||||
:ensure nil
|
||||
:commands (bookmark-set bookmark-jump bookmark-bmenu-list)
|
||||
:hook (bookmark-bmenu-mode . hl-line-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(add-hook 'bookmark-bmenu-mode-hook #'hl-line-mode)
|
||||
(setq bookmark-use-annotations nil)
|
||||
(setq bookmark-automatically-show-annotations nil)
|
||||
(setq bookmark-fringe-mark nil) ; Emacs 29 to hide bookmark fringe icon
|
||||
|
|
@ -223,33 +280,21 @@
|
|||
(setq bookmark-save-flag 1))
|
||||
|
||||
;;;; Registers (register.el)
|
||||
(use-package register
|
||||
:ensure nil
|
||||
:defer t ; its commands are autoloaded, so this will be loaded then
|
||||
:config
|
||||
(setq register-preview-delay 0.8
|
||||
register-preview-function #'register-preview-default)
|
||||
|
||||
(with-eval-after-load 'savehist
|
||||
(add-to-list 'savehist-additional-variables 'register-alist)))
|
||||
(setq register-preview-delay 0.5
|
||||
register-preview-function #'register-preview-default)
|
||||
|
||||
;;;; Auto revert mode
|
||||
(use-package autorevert
|
||||
:ensure nil
|
||||
:hook (after-init . global-auto-revert-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(global-auto-revert-mode 1)
|
||||
(setq auto-revert-verbose t))
|
||||
|
||||
;;;; Delete selection
|
||||
(use-package delsel
|
||||
:ensure nil
|
||||
:hook (after-init . delete-selection-mode))
|
||||
(prot-emacs-configure
|
||||
(delete-selection-mode 1))
|
||||
|
||||
;;;; Tooltips (tooltip-mode)
|
||||
(use-package tooltip
|
||||
:ensure nil
|
||||
:hook (after-init . tooltip-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(tooltip-mode 1)
|
||||
(setq tooltip-delay 0.5
|
||||
tooltip-short-delay 0.5
|
||||
x-gtk-use-system-tooltips t
|
||||
|
|
@ -260,10 +305,7 @@
|
|||
(no-special-glyphs . t))))
|
||||
|
||||
;;;; Display current time
|
||||
(use-package time
|
||||
:ensure nil
|
||||
:hook (after-init . display-time-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq display-time-format " %a %e %b, %H:%M ")
|
||||
;;;; Covered by `display-time-format'
|
||||
;; (setq display-time-24hr-format t)
|
||||
|
|
@ -286,60 +328,62 @@
|
|||
(format-time-string display-time-format now)
|
||||
'face 'display-time-date-and-time
|
||||
'help-echo (format-time-string "%a %b %e, %Y" now))
|
||||
" ")))
|
||||
" "))
|
||||
|
||||
(display-time-mode 1))
|
||||
|
||||
;;;; World clock (M-x world-clock)
|
||||
(use-package time
|
||||
:ensure nil
|
||||
:commands (world-clock)
|
||||
:config
|
||||
(setq display-time-world-list t)
|
||||
(prot-emacs-configure
|
||||
(setq zoneinfo-style-world-list ; M-x shell RET timedatectl list-timezones
|
||||
'(("America/Los_Angeles" "Los Angeles")
|
||||
("America/Vancouver" "Vancouver")
|
||||
("Canada/Pacific" "Canada/Pacific")
|
||||
("America/Chicago" "Chicago")
|
||||
("Brazil/Acre" "Rio Branco")
|
||||
("America/New_York" "New York")
|
||||
("Canada/Atlantic" "Canada/Atlantic")
|
||||
("Brazil/East" "Brasília")
|
||||
("UTC" "UTC")
|
||||
("Europe/Lisbon" "Lisbon")
|
||||
("Europe/Brussels" "Brussels")
|
||||
("Europe/Athens" "Athens")
|
||||
("Asia/Riyadh" "Riyadh")
|
||||
("Asia/Tehran" "Tehran")
|
||||
("Asia/Tbilisi" "Tbilisi")
|
||||
("Asia/Yekaterinburg" "Yekaterinburg")
|
||||
("Asia/Kolkata" "Kolkata")
|
||||
("Asia/Singapore" "Singapore")
|
||||
("Asia/Shanghai" "Shanghai")
|
||||
("Asia/Seoul" "Seoul")
|
||||
("Asia/Tokyo" "Tokyo")
|
||||
("Asia/Vladivostok" "Vladivostok")
|
||||
("Australia/Brisbane" "Brisbane")
|
||||
("Australia/Sydney" "Sydney")
|
||||
("Pacific/Auckland" "Auckland")))
|
||||
'(("America/Los_Angeles" "🇺🇸 Los Angeles")
|
||||
("America/Vancouver" "🇨🇦 Vancouver")
|
||||
("America/Mexico_City" "🇲🇽 Mexico City")
|
||||
("America/Denver" "🇺🇸 Denver")
|
||||
("America/Chicago" "🇺🇸 Chicago")
|
||||
("America/New_York" "🇺🇸 New York")
|
||||
("America/Toronto" "🇨🇦 Toronto")
|
||||
("America/Montreal" "🇨🇦 Montreal")
|
||||
("UTC" "🌐 UTC")
|
||||
("Europe/Lisbon" "🇵🇹 Lisbon")
|
||||
("Europe/London" "🇬🇧 London")
|
||||
("Europe/Paris" "🇫🇷 Paris")
|
||||
("Europe/Brussels" "🇧🇪 Brussels")
|
||||
("Europe/Berlin" "🇩🇪 Berlin")
|
||||
("Europe/Athens" "🇬🇷 Athens")
|
||||
("Asia/Nicosia" "🇨🇾 Lefkosia")
|
||||
("Asia/Istanbul" "🇹🇷 Istanbul")
|
||||
("Asia/Baghdad" "🇸🇾 Baghdad")
|
||||
("Asia/Riyadh" "🇸🇦 Riyadh")
|
||||
("Asia/Tehran" "🇮🇷 Tehran")
|
||||
("Asia/Tbilisi" "🇬🇪 Tbilisi")
|
||||
("Asia/Tashkent" "🇺🇿 Tashkent")
|
||||
("Asia/Karachi" "🇵🇰 Karachi")
|
||||
("Asia/Kolkata" "🇮🇳 Kolkata")
|
||||
("Asia/Dhaka" "🇧🇩 Dhaka")
|
||||
("Asia/Phnom_Penh" "🇰🇭 Phnom Penh")
|
||||
("Asia/Ho_Chi_Minh" "🇻🇳 Ho Chi Minh")
|
||||
("Asia/Singapore" "🇸🇬 Singapore")
|
||||
("Asia/Shanghai" "🇨🇳 Shanghai")
|
||||
("Asia/Seoul" "🇰🇷 Seoul")
|
||||
("Asia/Tokyo" "🇯🇵 Tokyo")
|
||||
("Australia/Brisbane" "🇦🇺 Brisbane")
|
||||
("Australia/Sydney" "🇦🇺 Sydney")
|
||||
("Pacific/Auckland" "🇳🇿 Auckland")))
|
||||
|
||||
;; All of the following variables are for Emacs 28
|
||||
(setq world-clock-sort-order nil) ; Emacs 31
|
||||
(setq world-clock-list t)
|
||||
(setq world-clock-time-format "%R %z (%Z) %A %d %B")
|
||||
(setq world-clock-time-format "%z %R %a %d %b (%Z)")
|
||||
(setq world-clock-buffer-name "*world-clock*") ; Placement handled by `display-buffer-alist'
|
||||
(setq world-clock-timer-enable t)
|
||||
(setq world-clock-timer-second 60))
|
||||
|
||||
;;;; `man' (manpages)
|
||||
(use-package man
|
||||
:ensure nil
|
||||
:commands (man)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq Man-notify-method 'pushy)) ; does not obey `display-buffer-alist'
|
||||
|
||||
;;;; `proced' (process monitor, similar to `top')
|
||||
(use-package proced
|
||||
:ensure nil
|
||||
:commands (proced)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq proced-auto-update-flag 'visible) ; Emacs 30 supports more the `visible' value
|
||||
(setq proced-enable-color-flag t) ; Emacs 29
|
||||
(setq proced-auto-update-interval 5)
|
||||
|
|
@ -347,31 +391,24 @@
|
|||
(setq proced-filter 'user))
|
||||
|
||||
;;;; Emacs server (allow emacsclient to connect to running session)
|
||||
(use-package server
|
||||
:ensure nil
|
||||
:defer 1
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq server-client-instructions nil)
|
||||
(unless (server-running-p)
|
||||
(require 'server)
|
||||
(unless (or (server-running-p) (daemonp))
|
||||
(server-start)))
|
||||
|
||||
;;; Substitute
|
||||
;; Another package of mine... Video demo:
|
||||
;; <https://protesilaos.com/codelog/2023-01-16-emacs-substitute-package-demo/>.
|
||||
(use-package substitute
|
||||
:ensure t
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install substitute)
|
||||
(require 'substitute)
|
||||
|
||||
;; Produce a message after the substitution that reports on what
|
||||
;; happened. It is a single line, like "Substituted `TARGET' with
|
||||
;; `SUBSTITUTE' N times across the buffer.
|
||||
:hook (substitute-post-replace . substitute-report-operation)
|
||||
:bind
|
||||
;; The mnemonic for the prefix is that M-# (or M-S-3) is close to
|
||||
;; M-% (or M-S-5).
|
||||
(("M-# s" . substitute-target-below-point) ; Forward motion like isearch (C-s)
|
||||
("M-# r" . substitute-target-above-point) ; Backward motion like isearch (C-r)
|
||||
("M-# d" . substitute-target-in-defun) ; "defun" mnemonic
|
||||
("M-# b" . substitute-target-in-buffer)) ; "buffer" mnemonic
|
||||
:config
|
||||
(add-hook 'substitute-post-replace-hook #'substitute-report-operation)
|
||||
|
||||
;; Set this to non-nil to highlight all occurrences of the current
|
||||
;; target.
|
||||
(setopt substitute-highlight t)
|
||||
|
|
@ -379,190 +416,114 @@
|
|||
;; Set this to t if you want to always treat the letter casing
|
||||
;; literally. Otherwise each command accepts a `C-u' prefix
|
||||
;; argument to do this on-demand.
|
||||
(setq substitute-fixed-letter-case nil))
|
||||
(setq substitute-fixed-letter-case nil)
|
||||
|
||||
(use-package goto-chg
|
||||
:ensure t
|
||||
:bind
|
||||
(("C-(" . goto-last-change)
|
||||
("C-)" . goto-last-change-reverse)))
|
||||
;; C-c s is occupied by `prot-scratch-buffer'.
|
||||
(define-key global-map (kbd "C-c r") #'substitute-prefix-map))
|
||||
|
||||
;;; Mark syntactic constructs efficiently if tree-sitter is available (expreg)
|
||||
(when (and (treesit-available-p) prot-emacs-treesitter-extras)
|
||||
(use-package expreg
|
||||
:ensure t
|
||||
:functions (prot/expreg-expand prot/expreg-expand-dwim)
|
||||
;; There is also an `expreg-contract' command, though I have no use for it.
|
||||
:bind ("C-M-SPC" . prot/expreg-expand-dwim) ; overrides `mark-sexp'
|
||||
:config
|
||||
(defun prot/expreg-expand (n)
|
||||
"Expand to N syntactic units, defaulting to 1 if none is provided interactively."
|
||||
(interactive "p")
|
||||
(dotimes (_ n)
|
||||
(expreg-expand)))
|
||||
|
||||
(defun prot/expreg-expand-dwim ()
|
||||
"Do-What-I-Mean `expreg-expand' to start with symbol or word.
|
||||
If over a real symbol, mark that directly, else start with a
|
||||
word. Fall back to regular `expreg-expand'."
|
||||
(interactive)
|
||||
(let ((symbol (bounds-of-thing-at-point 'symbol)))
|
||||
(cond
|
||||
((equal (bounds-of-thing-at-point 'word) symbol)
|
||||
(prot/expreg-expand 1))
|
||||
(symbol (prot/expreg-expand 2))
|
||||
(t (expreg-expand)))))))
|
||||
|
||||
;;; Visualise undo ring (`vundo')
|
||||
(use-package vundo
|
||||
:ensure t
|
||||
:defer 1
|
||||
:bind
|
||||
( :map vundo-mode-map
|
||||
("C-/" . vundo-backward)
|
||||
("C-?" . vundo-forward)
|
||||
("u" . vundo-backward)
|
||||
("U" . vundo-forward)
|
||||
("g" . vundo-goto-last-saved)
|
||||
("." . vundo-goto-last-saved)
|
||||
("h" . vundo-backward)
|
||||
("j" . vundo-next)
|
||||
("k" . vundo-previous)
|
||||
("l" . vundo-forward))
|
||||
:config
|
||||
(setq vundo-glyph-alist vundo-unicode-symbols)
|
||||
|
||||
(defvar prot/vundo-undo-functions '(undo undo-only undo-redo)
|
||||
"List of undo functions to check if we need to visualise the undo ring.")
|
||||
|
||||
(defvar prot/vundo-undo-command #'undo
|
||||
"Command to call if we are not going to visualise the undo ring.")
|
||||
|
||||
(defun prot/vundo-if-repeat-undo (&rest args)
|
||||
"Use `vundo' if the last command is among `prot/vundo-undo-functions'.
|
||||
In other words, start visualising the undo ring if we are going
|
||||
to be cycling through the edits."
|
||||
(interactive)
|
||||
(if (and (member last-command prot/vundo-undo-functions)
|
||||
(not undo-in-region))
|
||||
(call-interactively 'vundo)
|
||||
(apply args)))
|
||||
|
||||
(mapc
|
||||
(lambda (fn)
|
||||
(advice-add fn :around #'prot/vundo-if-repeat-undo))
|
||||
prot/vundo-undo-functions)
|
||||
|
||||
(with-eval-after-load 'pulsar
|
||||
(add-hook 'vundo-post-exit-hook #'pulsar-pulse-line-green)))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install goto-chg)
|
||||
(prot-emacs-keybind global-map
|
||||
"C-(" #'goto-last-change
|
||||
"C-)" #'goto-last-change-reverse))
|
||||
|
||||
;;; TMR May Ring (tmr is used to set timers)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/tmr>.
|
||||
(use-package tmr
|
||||
:ensure t
|
||||
:bind
|
||||
(("C-c t t" . tmr)
|
||||
("C-c t T" . tmr-with-description)
|
||||
("C-c t l" . tmr-tabulated-view) ; "list timers" mnemonic
|
||||
("C-c t c" . tmr-clone)
|
||||
("C-c t k" . tmr-cancel)
|
||||
("C-c t s" . tmr-reschedule)
|
||||
("C-c t e" . tmr-edit-description)
|
||||
("C-c t r" . tmr-remove)
|
||||
("C-c t R" . tmr-remove-finished))
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install tmr)
|
||||
(define-key global-map (kbd "C-c t") #'tmr-prefix-map)
|
||||
(setq tmr-sound-file "/usr/share/sounds/freedesktop/stereo/alarm-clock-elapsed.oga"
|
||||
tmr-notification-urgency 'normal
|
||||
tmr-description-list 'tmr-description-history))
|
||||
|
||||
;;; Pass interface (password-store)
|
||||
(use-package password-store
|
||||
:ensure t
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install password-store)
|
||||
|
||||
;; Mnemonic is the root of the "code" word (κώδικας). But also to add
|
||||
;; the password to the kill-ring. Other options are already taken.
|
||||
:bind ("C-c k" . password-store-copy)
|
||||
:config
|
||||
(setq password-store-time-before-clipboard-restore 30))
|
||||
(define-key global-map (kbd "C-c k") #'password-store-copy)
|
||||
|
||||
(use-package pass
|
||||
:ensure t
|
||||
:commands (pass))
|
||||
(setq password-store-time-before-clipboard-restore 30)
|
||||
|
||||
;;; Shell (M-x shell)
|
||||
(use-package shell
|
||||
:ensure nil
|
||||
:bind
|
||||
( :map global-map
|
||||
("<f1>" . shell) ; I don't use F1 for help commands
|
||||
:map shell-mode-map
|
||||
("C-c C-k" . comint-clear-buffer)
|
||||
("C-c C-w" . comint-write-output))
|
||||
:config
|
||||
;; Check my .bashrc which handles `comint-terminfo-terminal':
|
||||
;;
|
||||
;; # Default pager. The check for the terminal is useful for Emacs with
|
||||
;; # M-x shell (which is how I usually interact with bash these days).
|
||||
;; #
|
||||
;; # The COLORTERM is documented in (info "(emacs) General Variables").
|
||||
;; # I found the reference to `dumb-emacs-ansi' in (info "(emacs)
|
||||
;; # Connection Variables").
|
||||
;; if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ]
|
||||
;; then
|
||||
;; export PAGER="cat"
|
||||
;; alias less="cat"
|
||||
;; export TERM=dumb-emacs-ansi
|
||||
;; export COLORTERM=1
|
||||
;; else
|
||||
;; # Quit once you try to scroll past the end of the file.
|
||||
;; export PAGER="less --quit-at-eof"
|
||||
;; fi
|
||||
(prot-emacs-install pass))
|
||||
|
||||
(setq shell-command-prompt-show-cwd t) ; Emacs 27.1
|
||||
(setq ansi-color-for-comint-mode t)
|
||||
(setq shell-input-autoexpand 'input)
|
||||
(setq shell-highlight-undef-enable t) ; Emacs 29.1
|
||||
(setq shell-has-auto-cd nil) ; Emacs 29.1
|
||||
(setq shell-get-old-input-include-continuation-lines t) ; Emacs 30.1
|
||||
(setq shell-kill-buffer-on-exit t) ; Emacs 29.1
|
||||
(setq shell-completion-fignore '("~" "#" "%"))
|
||||
(setq-default comint-scroll-to-bottom-on-input t)
|
||||
(setq-default comint-scroll-to-bottom-on-output nil)
|
||||
(setq-default comint-input-autoexpand 'input)
|
||||
;;; Generic interface for shells or REPLs (comint)
|
||||
(prot-emacs-configure
|
||||
;; Support for OS-specific escape sequences such as what `ls
|
||||
;; --hyperlink' uses. I normally don't use those, but I am checking
|
||||
;; this to see if there are any obvious advantages/disadvantages.
|
||||
(add-hook 'comint-output-filter-functions #'comint-osc-process-output)
|
||||
|
||||
(setq ansi-color-for-comint-mode t) ; also see `ansi-color-for-compilation-mode'
|
||||
(setq comint-prompt-read-only t)
|
||||
(setq comint-buffer-maximum-size 9999)
|
||||
(setq comint-completion-autolist t)
|
||||
(setq comint-input-ignoredups t)
|
||||
(setq tramp-default-remote-shell "/bin/bash")
|
||||
(setq-default comint-scroll-to-bottom-on-input t)
|
||||
(setq-default comint-scroll-to-bottom-on-output nil)
|
||||
(setq-default comint-input-autoexpand 'input))
|
||||
|
||||
(setq shell-font-lock-keywords
|
||||
'(("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-builtin-face)
|
||||
("^[^ \t\n]+:.*" . font-lock-string-face)
|
||||
("^\\[[1-9][0-9]*\\]" . font-lock-constant-face)))
|
||||
;;; Compilation interface (M-x compile)
|
||||
(prot-emacs-configure
|
||||
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)
|
||||
(setq ansi-color-for-compilation-mode t)) ; also see `ansi-color-for-comint-mode'
|
||||
|
||||
;; Support for OS-specific escape sequences such as what `ls
|
||||
;; --hyperlink' uses. I normally don't use those, but I am checking
|
||||
;; this to see if there are any obvious advantages/disadvantages.
|
||||
(add-hook 'comint-output-filter-functions 'comint-osc-process-output))
|
||||
;;; Standard Unix Shell (M-x shell)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-autoload (prot-shell prot-shell-mode) "prot-shell")
|
||||
|
||||
(use-package prot-shell
|
||||
:ensure nil
|
||||
:hook (shell-mode . prot-shell-mode))
|
||||
(define-key global-map (kbd "<f1>") #'prot-shell) ; I don't use F1 for help commands
|
||||
|
||||
;;; Laptop settings
|
||||
(unless (directory-empty-p "/sys/class/power_supply/")
|
||||
(add-to-list 'default-frame-alist '(width . (text-pixels . 800)))
|
||||
(add-to-list 'default-frame-alist '(height . (text-pixels . 600)))
|
||||
(with-eval-after-load 'prot-shell
|
||||
(add-hook 'shell-mode-hook #'prot-shell-mode)
|
||||
|
||||
(use-package battery
|
||||
:ensure nil
|
||||
:hook (after-init . display-battery-mode)
|
||||
:config
|
||||
;;;; Show battery status on the mode line (battery.el)
|
||||
(prot-emacs-keybind shell-mode-map
|
||||
"C-c C-k" #'comint-clear-buffer
|
||||
"C-c C-w" #'comint-write-output)
|
||||
|
||||
;; Check my .bashrc which handles `comint-terminfo-terminal':
|
||||
;;
|
||||
;; # Default pager. The check for the terminal is useful for Emacs with
|
||||
;; # M-x shell (which is how I usually interact with bash these days).
|
||||
;; #
|
||||
;; # The COLORTERM is documented in (info "(emacs) General Variables").
|
||||
;; # I found the reference to `dumb-emacs-ansi' in (info "(emacs)
|
||||
;; # Connection Variables").
|
||||
;; if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ] || [ "$TERM" = "dumb-emacs-ansi" ] && [ "$INSIDE_EMACS" ]
|
||||
;; then
|
||||
;; export PAGER="cat"
|
||||
;; alias less="cat"
|
||||
;; export TERM=dumb-emacs-ansi
|
||||
;; export COLORTERM=1
|
||||
;; else
|
||||
;; # Quit once you try to scroll past the end of the file.
|
||||
;; export PAGER="less --quit-at-eof"
|
||||
;; fi
|
||||
(setq shell-command-prompt-show-cwd t) ; Emacs 27.1
|
||||
(setq shell-input-autoexpand 'input)
|
||||
(setq shell-highlight-undef-enable t) ; Emacs 29.1
|
||||
(setq shell-has-auto-cd nil) ; Emacs 29.1
|
||||
(setq shell-get-old-input-include-continuation-lines t) ; Emacs 30.1
|
||||
(setq shell-kill-buffer-on-exit t) ; Emacs 29.1
|
||||
(setq shell-completion-fignore '("~" "#" "%"))
|
||||
(setq tramp-default-remote-shell "/bin/bash")
|
||||
|
||||
(setq shell-font-lock-keywords
|
||||
'(("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-builtin-face)
|
||||
("^[^ \t\n]+:.*" . font-lock-string-face)
|
||||
("^\\[[1-9][0-9]*\\]" . font-lock-constant-face)))))
|
||||
|
||||
;;; Show battery status on the mode line with `display-battery-mode'
|
||||
(when prot-laptop-p
|
||||
(prot-emacs-configure
|
||||
(require 'battery)
|
||||
(setq battery-mode-line-format
|
||||
(cond
|
||||
((eq battery-status-function #'battery-linux-proc-acpi)
|
||||
"⏻%b%p%%,%d°C ")
|
||||
(battery-status-function
|
||||
"⏻%b%p%% ")))))
|
||||
"⏻%b%p%% ")))
|
||||
(display-battery-mode 1)))
|
||||
|
||||
(provide 'prot-emacs-essentials)
|
||||
|
|
|
|||
|
|
@ -1,215 +1,312 @@
|
|||
;;;; `ediff'
|
||||
(use-package ediff
|
||||
:ensure nil
|
||||
:commands (ediff-buffers ediff-files ediff-buffers3 ediff-files3)
|
||||
:init
|
||||
(setq ediff-split-window-function 'split-window-horizontally)
|
||||
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
|
||||
:config
|
||||
(setq ediff-keep-variants nil)
|
||||
(setq ediff-make-buffers-readonly-at-startup nil)
|
||||
(setq ediff-merge-revisions-with-ancestor t)
|
||||
(setq ediff-show-clashes-only t))
|
||||
|
||||
;;;; `project'
|
||||
(use-package project
|
||||
:ensure nil
|
||||
:bind
|
||||
(("C-x p ." . project-dired)
|
||||
("C-x p C-g" . keyboard-quit)
|
||||
("C-x p <return>" . project-dired)
|
||||
("C-x p <delete>" . project-forget-project))
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x p ." #'project-dired
|
||||
"C-x p C-g" #'keyboard-quit
|
||||
"C-x p <return>" #'project-dired
|
||||
"C-x p <delete>" #'project-forget-project)
|
||||
|
||||
(setopt project-switch-commands
|
||||
'((project-find-file "Find file")
|
||||
(project-find-regexp "Find regexp")
|
||||
(project-find-dir "Find directory")
|
||||
(project-find-regexp "Find regexp")
|
||||
(project-dired "Root dired")
|
||||
(project-vc-dir "VC-Dir")
|
||||
(project-shell "Shell")
|
||||
(project-any-command "Other command")
|
||||
(keyboard-quit "Quit")))
|
||||
(setq project-vc-extra-root-markers '(".project")) ; Emacs 29
|
||||
(setq project-key-prompt-style t) ; Emacs 30
|
||||
|
||||
(advice-add #'project-switch-project :after #'prot-common-clear-minibuffer-message))
|
||||
(advice-add #'project-switch-project :after #'prot-common-clear-minibuffer-message)
|
||||
|
||||
(use-package prot-project
|
||||
:ensure nil
|
||||
;; Also check the command `prot-project-in-tab'. I do not use it
|
||||
;; because I prefer to manage my buffers in frames, with my
|
||||
;; `beframe' package.
|
||||
:bind
|
||||
( :map project-prefix-map
|
||||
("p" . prot-project-switch)))
|
||||
(autoload #'prot-project-maybe-in-tab "prot-project")
|
||||
(autoload #'prot-project-switch "prot-project")
|
||||
(define-key project-prefix-map (kbd "p") #'prot-project-maybe-in-tab))
|
||||
|
||||
;;;; `diff-mode'
|
||||
(use-package diff-mode
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq diff-default-read-only t)
|
||||
(setq diff-advance-after-apply-hunk t)
|
||||
(setq diff-update-on-the-fly t)
|
||||
;; The following are from Emacs 27.1
|
||||
(setq diff-refine nil) ; I do it on demand, with my `agitate' package (more below)
|
||||
(setq diff-font-lock-prettify t) ; I think nil is better for patches, but let me try this for a while
|
||||
(setq diff-font-lock-syntax 'hunk-also))
|
||||
(setq diff-font-lock-syntax nil))
|
||||
|
||||
;;; Version control framework (vc.el, vc-git.el, and more)
|
||||
(use-package vc
|
||||
:ensure nil
|
||||
:bind
|
||||
(;; NOTE: I override lots of the defaults
|
||||
:map global-map
|
||||
("C-x v B" . vc-annotate) ; Blame mnemonic
|
||||
("C-x v e" . vc-ediff)
|
||||
("C-x v k" . vc-delete-file) ; 'k' for kill==>delete is more common
|
||||
("C-x v G" . vc-log-search) ; git log --grep
|
||||
("C-x v t" . vc-create-tag)
|
||||
("C-x v d" . vc-diff)
|
||||
("C-x v ." . vc-dir-root) ; `vc-dir-root' is from Emacs 28
|
||||
("C-x v <return>" . vc-dir-root)
|
||||
:map vc-dir-mode-map
|
||||
("t" . vc-create-tag)
|
||||
("O" . vc-log-outgoing)
|
||||
("o" . vc-dir-find-file-other-window)
|
||||
("d" . vc-diff) ; parallel to D: `vc-root-diff'
|
||||
("k" . vc-dir-delete-file)
|
||||
("G" . vc-revert)
|
||||
:map vc-git-stash-shared-map
|
||||
("a" . vc-git-stash-apply-at-point)
|
||||
("c" . vc-git-stash) ; "create" named stash
|
||||
("k" . vc-git-stash-delete-at-point) ; symmetry with `vc-dir-delete-file'
|
||||
("p" . vc-git-stash-pop-at-point)
|
||||
("s" . vc-git-stash-snapshot)
|
||||
:map vc-annotate-mode-map
|
||||
("M-q" . vc-annotate-toggle-annotation-visibility)
|
||||
("C-c C-c" . vc-annotate-goto-line)
|
||||
("<return>" . vc-annotate-find-revision-at-line)
|
||||
:map log-edit-mode-map
|
||||
("M-s" . nil) ; I use M-s for my search commands
|
||||
("M-r" . nil) ; I use `consult-history'
|
||||
:map log-view-mode-map
|
||||
("<tab>" . log-view-toggle-entry-display)
|
||||
("<return>" . log-view-find-revision)
|
||||
("s" . vc-log-search)
|
||||
("o" . vc-log-outgoing)
|
||||
("f" . vc-log-incoming)
|
||||
("F" . vc-update)
|
||||
("P" . vc-push))
|
||||
:init
|
||||
;;;; `diff-hl'
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install diff-hl)
|
||||
|
||||
(setq diff-hl-draw-borders nil)
|
||||
(setq diff-hl-update-async t)
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x v <up>" #'diff-hl-previous-hunk
|
||||
"C-x v <down>" #'diff-hl-next-hunk)
|
||||
|
||||
(prot-emacs-hook (prog-mode-hook vc-dir-mode-hook) turn-on-diff-hl-mode)
|
||||
|
||||
(with-eval-after-load 'magit
|
||||
(add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh)))
|
||||
|
||||
;;;; `ediff'
|
||||
(prot-emacs-configure
|
||||
(setq ediff-split-window-function 'split-window-horizontally)
|
||||
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
|
||||
(setq ediff-keep-variants nil)
|
||||
(setq ediff-make-buffers-readonly-at-startup nil)
|
||||
(setq ediff-merge-revisions-with-ancestor t)
|
||||
(setq ediff-show-clashes-only t)
|
||||
|
||||
(with-eval-after-load 'outline
|
||||
(add-hook 'ediff-prepare-buffer-hook #'outline-show-all))
|
||||
|
||||
(prot-emacs-autoload
|
||||
(prot-ediff-visible-buffers-2
|
||||
prot-ediff-visible-buffers-3
|
||||
prot-ediff-store-layout
|
||||
prot-ediff-restore-layout)
|
||||
"prot-ediff")
|
||||
;; The C-x v prefix is for all "version control" commands that are
|
||||
;; already built into Emacs. It makes sense to extend it for this
|
||||
;; use-case.
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x v 2" #'prot-ediff-visible-buffers-2
|
||||
"C-x v 3" #'prot-ediff-visible-buffers-3)
|
||||
(add-hook 'ediff-before-setup-hook #'prot-ediff-store-layout)
|
||||
(add-hook 'ediff-quit-hook #'prot-ediff-restore-layout))
|
||||
|
||||
;;;; `smerge-mode'
|
||||
(prot-emacs-configure
|
||||
(setq smerge-diff-buffer-name "*smerge-diff*")
|
||||
(setq smerge-refine-shadow-cursor nil)) ; Emacs 31
|
||||
|
||||
;;; Version control framework (vc.el, vc-git.el, and more)
|
||||
(prot-emacs-configure
|
||||
(setq vc-follow-symlinks t)
|
||||
:config
|
||||
;; Those offer various types of functionality, such as blaming,
|
||||
;; viewing logs, showing a dedicated buffer with changes to affected
|
||||
;; files.
|
||||
(require 'vc-annotate)
|
||||
(require 'vc-dir)
|
||||
(require 'vc-git)
|
||||
(require 'add-log)
|
||||
(require 'log-view)
|
||||
|
||||
;; I only use Git. If I ever need another, I will include it here.
|
||||
;; This may have an effect on performance, as Emacs will not try to
|
||||
;; check for a bunch of backends.
|
||||
(setq vc-handled-backends '(Git))
|
||||
(with-eval-after-load 'vc
|
||||
;; Those offer various types of functionality, such as blaming,
|
||||
;; viewing logs, showing a dedicated buffer with changes to affected
|
||||
;; files.
|
||||
(require 'vc-annotate)
|
||||
(require 'vc-dir)
|
||||
(require 'vc-git)
|
||||
(require 'add-log)
|
||||
(require 'log-view)
|
||||
|
||||
;; This one is for editing commit messages.
|
||||
(require 'log-edit)
|
||||
(setq log-edit-confirm 'changed)
|
||||
(setq log-edit-keep-buffer nil)
|
||||
(setq log-edit-require-final-newline t)
|
||||
(setq log-edit-setup-add-author nil)
|
||||
;; I can see the files from the Diff with C-c C-d
|
||||
(remove-hook 'log-edit-hook #'log-edit-show-files)
|
||||
;; I only use Git. If I ever need another, I will include it here.
|
||||
;; This may have an effect on performance, as Emacs will not try to
|
||||
;; check for a bunch of backends.
|
||||
(setq vc-handled-backends '(Git))
|
||||
|
||||
(setq vc-find-revision-no-save t)
|
||||
(setq vc-annotate-display-mode 'scale) ; scale to oldest
|
||||
;; I use a different account for git commits
|
||||
(setq add-log-mailing-address "info@protesilaos.com")
|
||||
(setq add-log-keep-changes-together t)
|
||||
(setq vc-git-diff-switches '("--patch-with-stat" "--histogram"))
|
||||
(setq vc-git-log-switches '("--stat"))
|
||||
(setq vc-git-print-log-follow t)
|
||||
(setq vc-git-revision-complete-only-branches nil) ; Emacs 28
|
||||
(setq vc-git-root-log-format
|
||||
`("%d %h %ai %an: %s"
|
||||
;; The first shy group matches the characters drawn by --graph.
|
||||
;; We use numbered groups because `log-view-message-re' wants the
|
||||
;; revision number to be group 1.
|
||||
,(concat "^\\(?:[*/\\|]+\\)\\(?:[*/\\| ]+\\)?"
|
||||
"\\(?2: ([^)]+) \\)?\\(?1:[0-9a-z]+\\) "
|
||||
"\\(?4:[0-9]\\{4\\}-[0-9-]\\{4\\}[0-9\s+:-]\\{16\\}\\) "
|
||||
"\\(?3:.*?\\):")
|
||||
((1 'log-view-message)
|
||||
(2 'change-log-list nil lax)
|
||||
(3 'change-log-name)
|
||||
(4 'change-log-date))))
|
||||
(setq vc-dir-save-some-buffers-on-revert t) ; Emacs 31
|
||||
(setq vc-use-incoming-outgoing-prefixes t) ; Emacs 31
|
||||
(setq vc-async-checkin t) ; Emacs 31
|
||||
(setq vc-display-failed-async-commands t) ; Emacs 31
|
||||
(setq vc-dir-show-key-binding-hints nil) ; Emacs 32
|
||||
|
||||
;; These two are from Emacs 29
|
||||
(setq vc-git-log-edit-summary-target-len 50)
|
||||
(setq vc-git-log-edit-summary-max-len 70))
|
||||
;; This one is for editing commit messages.
|
||||
(require 'log-edit)
|
||||
(setq log-edit-confirm 'changed)
|
||||
(setq log-edit-keep-buffer nil)
|
||||
(setq log-edit-require-final-newline t)
|
||||
(setq log-edit-setup-add-author nil)
|
||||
|
||||
;;; Agitate
|
||||
;; A package of mine to complement VC and friends. Read the manual
|
||||
;; here: <https://protesilaos.com/emacs/agitate>.
|
||||
(use-package agitate
|
||||
:ensure t
|
||||
:after vc
|
||||
:hook (diff-mode . agitate-diff-enable-outline-minor-mode)
|
||||
:bind
|
||||
:map global-map
|
||||
(("C-x v =" . agitate-diff-buffer-or-file) ; replace `vc-diff'
|
||||
("C-x v g" . agitate-vc-git-grep) ; replace `vc-annotate'
|
||||
("C-x v f" . agitate-vc-git-find-revision)
|
||||
("C-x v s" . agitate-vc-git-show)
|
||||
("C-x v w" . agitate-vc-git-kill-commit-message)
|
||||
("C-x v p p" . agitate-vc-git-format-patch-single)
|
||||
("C-x v p n" . agitate-vc-git-format-patch-n-from-head)
|
||||
:map diff-mode-map
|
||||
("C-c C-b" . agitate-diff-refine-cycle) ; replace `diff-refine-hunk'
|
||||
("C-c C-n" . agitate-diff-narrow-dwim)
|
||||
("L" . vc-print-root-log)
|
||||
;; Emacs 29 can use C-x v v in diff buffers, which is great, but now I
|
||||
;; need quick access to it...
|
||||
("v" . vc-next-action)
|
||||
:map log-view-mode-map
|
||||
("w" . agitate-log-view-kill-revision)
|
||||
("W" . agitate-log-view-kill-revision-expanded)
|
||||
:map vc-git-log-view-mode-map
|
||||
("c" . agitate-vc-git-format-patch-single)
|
||||
:map log-edit-mode-map
|
||||
("C-c C-i C-n" . agitate-log-edit-insert-file-name)
|
||||
;; See user options `agitate-log-edit-emoji-collection' and
|
||||
;; `agitate-log-edit-conventional-commits-collection'.
|
||||
("C-c C-i C-e" . agitate-log-edit-emoji-commit)
|
||||
("C-c C-i C-c" . agitate-log-edit-conventional-commit))
|
||||
:config
|
||||
(advice-add #'vc-git-push :override #'agitate-vc-git-push-prompt-for-remote)
|
||||
(prot-emacs-hook
|
||||
log-edit-hook
|
||||
(log-edit-insert-message-template
|
||||
log-edit-maybe-show-diff)
|
||||
nil
|
||||
log-edit)
|
||||
|
||||
(setq agitate-log-edit-informative-show-root-log nil
|
||||
agitate-log-edit-informative-show-files nil)
|
||||
(setq vc-find-revision-no-save t)
|
||||
(setq vc-annotate-display-mode 'scale) ; scale to oldest
|
||||
;; I use a different account for git commits
|
||||
(setq add-log-mailing-address "info@protesilaos.com")
|
||||
(setq add-log-keep-changes-together t)
|
||||
(setq vc-git-diff-switches '("--patch-with-stat" "--histogram"))
|
||||
(setq vc-git-log-switches '("--stat"))
|
||||
(setq vc-git-print-log-follow t)
|
||||
(setq vc-git-revision-complete-only-branches nil) ; Emacs 28
|
||||
(setq vc-git-root-log-format
|
||||
`("%d %h %ai %an: %s"
|
||||
;; The first shy group matches the characters drawn by --graph.
|
||||
;; We use numbered groups because `log-view-message-re' wants the
|
||||
;; revision number to be group 1.
|
||||
,(concat "^\\(?:[*/\\|]+\\)\\(?:[*/\\| ]+\\)?"
|
||||
"\\(?2: ([^)]+) \\)?\\(?1:[0-9a-z]+\\) "
|
||||
"\\(?4:[0-9]\\{4\\}-[0-9-]\\{4\\}[0-9\s+:-]\\{16\\}\\) "
|
||||
"\\(?3:.*?\\):")
|
||||
((1 'log-view-message)
|
||||
(2 'change-log-list nil lax)
|
||||
(3 'change-log-name)
|
||||
(4 'change-log-date))))
|
||||
|
||||
(agitate-log-edit-informative-mode 1))
|
||||
;; These two are from Emacs 29
|
||||
(setq vc-git-log-edit-summary-target-len 50)
|
||||
(setq vc-git-log-edit-summary-max-len 70)
|
||||
|
||||
(define-advice vc-push (:around (&rest args) prot)
|
||||
(let ((current-window (selected-window)))
|
||||
(apply args)
|
||||
(select-window current-window)))
|
||||
|
||||
(define-advice vc-pull (:around (&rest args) prot)
|
||||
(let ((current-window (selected-window)))
|
||||
(apply args)
|
||||
(select-window current-window)))
|
||||
|
||||
(defun prot/vc-diff-dwim ()
|
||||
"Show diff of buffer against file or against VC history."
|
||||
(interactive)
|
||||
(if-let* ((buffer (current-buffer))
|
||||
(_ (buffer-modified-p buffer)))
|
||||
(diff-buffer-with-file buffer)
|
||||
(call-interactively #'vc-diff)))
|
||||
|
||||
(defvar prot/vc-git-grep-history nil
|
||||
"Minibuffer history for `prot/vc-git-grep'.")
|
||||
|
||||
(defun prot/vc-git-grep (directory regexp)
|
||||
"Use `vc-git-grep' with REGEXP in the current root Git DIRECTORY."
|
||||
(interactive
|
||||
(let ((directory (or (vc-root-dir)
|
||||
(locate-dominating-file "." ".git")
|
||||
(user-error "No VC root available"))))
|
||||
(list
|
||||
directory
|
||||
(read-regexp
|
||||
(format "vc-git-grep for REGEXP in `%s': "
|
||||
(propertize directory 'face 'warning))
|
||||
nil 'prot/vc-git-grep-history))))
|
||||
(vc-git-grep regexp "*" directory))
|
||||
|
||||
;; NOTE: I override lots of the defaults
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x v B" #'vc-annotate ; Blame mnemonic
|
||||
"C-x v g" #'prot/vc-git-grep ; override original `vc-annotate' key
|
||||
"C-x v e" #'vc-ediff
|
||||
"C-x v k" #'vc-delete-file ; 'k' for kill==>delete is more common
|
||||
"C-x v G" #'vc-log-search ; git log --grep
|
||||
"C-x v t" #'vc-create-tag
|
||||
"C-x v c" #'vc-clone ; Emacs 31
|
||||
"C-x v d" #'prot/vc-diff-dwim
|
||||
"C-x v ." #'vc-dir-root ; `vc-dir-root' is from Emacs 28
|
||||
"C-x v <return>" #'vc-dir-root)
|
||||
(prot-emacs-keybind vc-dir-mode-map
|
||||
"t" #'vc-create-tag
|
||||
"I" #'vc-log-incoming
|
||||
"O" #'vc-log-outgoing
|
||||
"o" #'vc-dir-find-file-other-window
|
||||
"d" #'vc-diff ; parallel to D: `vc-root-diff'
|
||||
"k" #'vc-dir-delete-file
|
||||
"G" #'vc-revert)
|
||||
(prot-emacs-keybind vc-git-stash-shared-map
|
||||
"a" #'vc-git-stash-apply-at-point
|
||||
"c" #'vc-git-stash ; "create" named stash
|
||||
"k" #'vc-git-stash-delete-at-point ; symmetry with `vc-dir-delete-file'
|
||||
"p" #'vc-git-stash-pop-at-point
|
||||
"s" #'vc-git-stash-snapshot)
|
||||
(prot-emacs-keybind vc-annotate-mode-map
|
||||
"M-q" #'vc-annotate-toggle-annotation-visibility
|
||||
"C-c C-c" #'vc-annotate-goto-line
|
||||
"<return>" #'vc-annotate-find-revision-at-line)
|
||||
(prot-emacs-keybind log-edit-mode-map
|
||||
"M-s" nil ; I use M-s for my search commands
|
||||
"M-r" nil)
|
||||
(prot-emacs-keybind log-view-mode-map
|
||||
"<tab>" #'log-view-toggle-entry-display
|
||||
"<return>" #'log-view-find-revision
|
||||
"s" #'vc-log-search))
|
||||
|
||||
(defun prot/modus-vc-annotate ()
|
||||
(modus-themes-with-colors
|
||||
(setq vc-annotate-background-mode nil)
|
||||
(setq vc-annotate-very-old-color fg-dim)
|
||||
(setq vc-annotate-color-map
|
||||
`(( 20. . ,red)
|
||||
( 40. . ,red-cooler)
|
||||
( 60. . ,red-warmer)
|
||||
( 80. . ,yellow-warmer)
|
||||
(100. . ,yellow)
|
||||
(120. . ,yellow-cooler)
|
||||
(140. . ,green-warmer)
|
||||
(160. . ,green)
|
||||
(180. . ,green-cooler)
|
||||
(200. . ,cyan-cooler)
|
||||
(220. . ,cyan-warmer)
|
||||
(240. . ,cyan)
|
||||
(260. . ,blue-warmer)
|
||||
(280. . ,blue)
|
||||
(300. . ,blue-cooler)
|
||||
(320. . ,blue-intense)
|
||||
(340. . ,magenta-cooler)
|
||||
(360. . ,fg-dim)))))
|
||||
|
||||
(when (memq prot-emacs-load-theme-family '(modus ef standard))
|
||||
(with-eval-after-load 'vc-annotate
|
||||
(prot/modus-vc-annotate)
|
||||
(add-hook 'modus-themes-after-load-theme-hook #'prot/modus-vc-annotate))))
|
||||
|
||||
;;; Interactive and powerful git front-end (Magit)
|
||||
(use-package magit
|
||||
:ensure t
|
||||
:bind ("C-c g" . magit-status)
|
||||
:init
|
||||
(prot-emacs-configure
|
||||
(setq transient-show-popup 0.5)
|
||||
|
||||
(prot-emacs-install magit)
|
||||
|
||||
;; Let `display-buffer-alist' do its job
|
||||
(setq magit-display-buffer-function #'display-buffer)
|
||||
|
||||
(define-key global-map (kbd "C-c g") #'magit-status)
|
||||
|
||||
(with-eval-after-load 'magit
|
||||
(prot-emacs-keybind magit-mode-map
|
||||
"C-w" #'nil
|
||||
"M-w" #'nil))
|
||||
|
||||
(setq magit-define-global-key-bindings nil)
|
||||
(setq magit-section-visibility-indicator '("⮧"))
|
||||
:config
|
||||
(setq magit-section-visibility-indicators
|
||||
`(;; (magit-fringe-bitmap> . magit-fringe-bitmapv)
|
||||
(" ▼" . t))) ; same as `org-ellipsis'
|
||||
|
||||
;; Show icons for files in the Magit status and other buffers. This
|
||||
;; is from my prot-icons.el.
|
||||
(with-eval-after-load 'prot-icons
|
||||
(setq magit-format-file-function
|
||||
(lambda (_kind file face &rest _)
|
||||
(let ((icon (prot-icons-get-file-icon file)))
|
||||
(format "%s %s" icon (propertize file 'font-lock-face face))))))
|
||||
|
||||
(setq magit-diff-refine-hunk t)
|
||||
(setq magit-diff-fontify-hunk nil)
|
||||
(setq magit-diff-refine-ignore-whitespace t)
|
||||
|
||||
(setq magit-log-auto-more t)
|
||||
|
||||
(setq magit-repository-directories
|
||||
'(("~/Git/Projects" . 1)))
|
||||
(setq magit-repolist-columns
|
||||
`(("Name" 25 ,#'magit-repolist-column-ident)
|
||||
("Version" 15 ,#'magit-repolist-column-version
|
||||
((:sort magit-repolist-version<)))
|
||||
("Unpulled" 10 ,#'magit-repolist-column-unpulled-from-upstream
|
||||
((:help-echo "Upstream changes not in branch")
|
||||
(:right-align t)
|
||||
(:sort <)))
|
||||
("Unpushed" 10 ,#'magit-repolist-column-unpushed-to-upstream
|
||||
((:help-echo "Local changes not in upstream")
|
||||
(:right-align t)
|
||||
(:sort <)))
|
||||
("Path" 99 ,#'magit-repolist-column-path)))
|
||||
|
||||
(setq git-commit-summary-max-length 50)
|
||||
;; NOTE 2023-01-24: I used to also include `overlong-summary-line'
|
||||
;; in this list, but I realised I do not need it. My summaries are
|
||||
;; always in check. When I exceed the limit, it is for a good
|
||||
;; reason.
|
||||
(setq git-commit-style-convention-checks '(non-empty-second-line))
|
||||
|
||||
(setq magit-diff-refine-hunk t)
|
||||
|
||||
(setq magit-repository-directories
|
||||
'(("~/Git/Projects" . 1))))
|
||||
(setq git-commit-major-mode #'text-mode))
|
||||
|
||||
(provide 'prot-emacs-git)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
;; This is deprecated code as I stopped using Gnus a long time ago. I
|
||||
;; keep it here for when I need to test it for my themes (`ef-themes',
|
||||
;; `modus-themes', `standard-themes').
|
||||
;; `modus-themes', `standard-themes', `doric-themes').
|
||||
|
||||
|
||||
(use-package gnus
|
||||
|
|
@ -129,7 +129,7 @@
|
|||
;; name, this will use the contents of the "To:" field, prefixed by
|
||||
;; the string I specify. Useful when checking your "Sent" summary or
|
||||
;; a mailing list you participate in.
|
||||
(setq gnus-ignored-from-addresses "Protesilaos Stavrou")
|
||||
(setq gnus-ignored-from-addresses "Protesilaos")
|
||||
(setq gnus-summary-to-prefix "To: ")
|
||||
|
||||
(setq gnus-summary-line-format "%U%R %-18,18&user-date; %4L:%-25,25f %B%s\n")
|
||||
|
|
|
|||
|
|
@ -1,23 +1,46 @@
|
|||
;;; Icons
|
||||
(use-package nerd-icons
|
||||
:ensure t)
|
||||
;; NOTE 2025-12-21: Also see the `prot-minibuffer.el' and my
|
||||
;; `completion-category-overrides' for completion-related icons.
|
||||
(prot-emacs-configure
|
||||
(require 'prot-icons)
|
||||
|
||||
(use-package nerd-icons-completion
|
||||
:ensure t
|
||||
:after marginalia
|
||||
:config
|
||||
(nerd-icons-completion-marginalia-setup)
|
||||
(nerd-icons-completion-mode 1))
|
||||
(setq prot-icons-style 'emoji) ; `alphabetic', `symbolic', or `emoji'
|
||||
|
||||
(use-package nerd-icons-corfu
|
||||
:ensure t
|
||||
:after corfu
|
||||
:config
|
||||
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
|
||||
(prot-icons-dired-mode 1)
|
||||
(prot-icons-xref-mode 1)
|
||||
|
||||
(use-package nerd-icons-dired
|
||||
:ensure t
|
||||
:hook
|
||||
(dired-mode . nerd-icons-dired-mode))
|
||||
(when (and prot-emacs-load-theme-family
|
||||
(eq prot-icons-style 'alphabetic))
|
||||
(cond
|
||||
((memq prot-emacs-load-theme-family '(modus ef standard))
|
||||
(defun prot/icons-set-faces ()
|
||||
(modus-themes-with-colors
|
||||
(custom-set-faces
|
||||
`(prot-icons-icon ((,c :inherit (bold fixed-pitch) :box (:line-width (1 . -1)) :inverse-video t)))
|
||||
`(prot-icons-directory ((,c :inherit bold :foreground ,accent-0)))
|
||||
`(prot-icons-gray ((,c :inherit bold :foreground ,fg-dim)))
|
||||
`(prot-icons-red ((,c :inherit prot-icons-icon :foreground ,red)))
|
||||
`(prot-icons-green ((,c :inherit prot-icons-icon :foreground ,green-warmer)))
|
||||
`(prot-icons-yellow ((,c :inherit prot-icons-icon :foreground ,yellow)))
|
||||
`(prot-icons-blue ((,c :inherit prot-icons-icon :foreground ,blue-cooler)))
|
||||
`(prot-icons-magenta ((,c :inherit prot-icons-icon :foreground ,magenta-cooler)))
|
||||
`(prot-icons-cyan ((,c :inherit prot-icons-icon :foreground ,cyan))))))
|
||||
(add-hook 'modus-themes-after-load-theme-hook #'prot/icons-set-faces))
|
||||
((eq prot-emacs-load-theme-family 'doric)
|
||||
(defun prot/icons-set-faces ()
|
||||
(doric-themes-with-colors
|
||||
(custom-set-faces
|
||||
`(prot-icons-icon ((t :inherit (bold fixed-pitch) :box (:line-width (1 . -1)) :inverse-video t)))
|
||||
`(prot-icons-directory ((t :inherit bold :foreground ,fg-accent)))
|
||||
`(prot-icons-gray ((t :inherit bold :foreground ,fg-shadow-subtle)))
|
||||
`(prot-icons-red ((t :inherit prot-icons-icon :foreground ,fg-red)))
|
||||
`(prot-icons-green ((t :inherit prot-icons-icon :foreground ,fg-green)))
|
||||
`(prot-icons-yellow ((t :inherit prot-icons-icon :foreground ,fg-yellow)))
|
||||
`(prot-icons-blue ((t :inherit prot-icons-icon :foreground ,fg-blue)))
|
||||
`(prot-icons-magenta ((t :inherit prot-icons-icon :foreground ,fg-magenta)))
|
||||
`(prot-icons-cyan ((t :inherit prot-icons-icon :foreground ,fg-cyan))))))
|
||||
(add-hook 'doric-themes-after-load-theme-hook #'prot/icons-set-faces)))
|
||||
|
||||
(prot/icons-set-faces)))
|
||||
|
||||
(provide 'prot-emacs-icons)
|
||||
|
|
|
|||
|
|
@ -1,379 +1,400 @@
|
|||
;;;; Tabs, indentation, and the TAB key
|
||||
(use-package emacs
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq tab-always-indent 'complete)
|
||||
(setq tab-first-completion 'word-or-paren-or-punct) ; Emacs 27
|
||||
(setq tab-first-completion nil) ; Emacs 27
|
||||
(setq-default tab-width 4
|
||||
indent-tabs-mode nil))
|
||||
|
||||
;;;; Disable "electric" behaviour
|
||||
(use-package electric
|
||||
:ensure nil
|
||||
:hook
|
||||
(prog-mode . electric-indent-local-mode)
|
||||
:config
|
||||
;; I don't like auto indents in Org and related. They are okay for
|
||||
;; programming.
|
||||
(electric-pair-mode -1)
|
||||
;;;; Emacs Lisp major mode
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'elisp-mode
|
||||
(prot-emacs-keybind emacs-lisp-mode-map
|
||||
"C-c C-b" nil ; I do not want to byte compile the buffer
|
||||
"C-c C-f" nil) ; .. nor the file
|
||||
(prot-emacs-keybind lisp-interaction-mode-map
|
||||
"C-c C-b" nil
|
||||
"C-c C-f" nil)
|
||||
|
||||
;; All of these are from Emacs 31.
|
||||
(setq elisp-eldoc-funcall-with-docstring-length 'short)
|
||||
(setq elisp-eldoc-docstring-length-limit 1000)
|
||||
(set-default-toplevel-value 'lexical-binding t) ; Emacs 31
|
||||
|
||||
(dolist (package prot-emacs-my-packages)
|
||||
(add-to-list 'elisp-flymake-byte-compile-load-path (expand-file-name (format "%s" package) "/home/prot/Git/Projects/")))
|
||||
|
||||
(require 'prot-elisp)
|
||||
|
||||
(prot-emacs-keybind emacs-lisp-mode-map
|
||||
"C-j" #'prot-elisp-eval-and-print-last-sexp ; overrides `electric-newline-and-maybe-indent'
|
||||
"C-c C-p" #'prot-elisp-pp-macroexpand-last-sexp
|
||||
"C-M-w" #'prot-elisp-copy-current-definition) ; overrides `append-next-kill'
|
||||
(prot-emacs-keybind lisp-interaction-mode-map
|
||||
"C-j" #'prot-elisp-eval-and-print-last-sexp ; overrides `eval-print-last-sexp'
|
||||
"C-c C-p" #'prot-elisp-pp-macroexpand-last-sexp
|
||||
"C-M-w" #'prot-elisp-copy-current-definition))) ; overrides `append-next-kill'
|
||||
|
||||
;;;; The "electric" behaviour
|
||||
(prot-emacs-configure
|
||||
(electric-pair-mode 1)
|
||||
(electric-indent-mode 1)
|
||||
(electric-quote-mode -1)
|
||||
(electric-indent-mode -1))
|
||||
|
||||
(defun prot/electric-indent-mode-disable ()
|
||||
"Disable `electric-indent-local-mode'."
|
||||
(electric-indent-local-mode -1))
|
||||
|
||||
(add-hook 'org-mode-hook #'prot/electric-indent-mode-disable))
|
||||
|
||||
;;;; Parentheses (show-paren-mode)
|
||||
(use-package paren
|
||||
:ensure nil
|
||||
:hook (prog-mode . show-paren-local-mode)
|
||||
:config
|
||||
(setq show-paren-style 'parenthesis)
|
||||
(setq show-paren-when-point-in-periphery nil)
|
||||
(setq show-paren-when-point-inside-paren nil)
|
||||
(setq show-paren-context-when-offscreen 'overlay)) ; Emacs 29
|
||||
(prot-emacs-configure
|
||||
(add-hook 'prog-mode-hook #'show-paren-local-mode)
|
||||
(with-eval-after-load 'paren
|
||||
(setq show-paren-style 'parenthesis)
|
||||
(setq show-paren-when-point-in-periphery nil)
|
||||
(setq show-paren-when-point-inside-paren nil)
|
||||
(setq show-paren-context-when-offscreen 'overlay) ; Emacs 29
|
||||
(setq show-paren-not-in-comments-or-strings nil))) ; Emacs 31
|
||||
|
||||
;;;; Plain text (text-mode)
|
||||
(use-package text-mode
|
||||
:ensure nil
|
||||
:mode "\\`\\(README\\|CHANGELOG\\|COPYING\\|LICENSE\\)\\'"
|
||||
:hook
|
||||
((text-mode . turn-on-auto-fill)
|
||||
(prog-mode . (lambda () (setq-local sentence-end-double-space t))))
|
||||
:config
|
||||
(setq sentence-end-double-space nil)
|
||||
(setq sentence-end-without-period nil)
|
||||
(setq colon-double-space nil)
|
||||
(setq use-hard-newlines nil)
|
||||
(setq adaptive-fill-mode t))
|
||||
(prot-emacs-configure
|
||||
(add-to-list 'auto-mode-alist '("\\`\\(README\\|CHANGELOG\\|COPYING\\|LICENSE\\)\\'" . text-mode))
|
||||
|
||||
;;;; Arch Linux and AUR package scripts (sh-mode)
|
||||
(use-package sh-script
|
||||
:ensure nil
|
||||
:mode ("PKGBUILD" . sh-mode))
|
||||
(add-hook 'text-mode-hook #'turn-on-auto-fill)
|
||||
(add-hook 'prog-mode-hook (lambda () (setq-local sentence-end-double-space t)))
|
||||
|
||||
;;;; SystemD and other configuration files (conf-mode)
|
||||
(use-package conf-mode
|
||||
:ensure nil
|
||||
:mode ("\\`dircolors\\'" "\\.\\(service\\|timer\\)\\'"))
|
||||
(with-eval-after-load 'text-mode
|
||||
(setq sentence-end-double-space nil)
|
||||
(setq sentence-end-without-period nil)
|
||||
(setq colon-double-space nil)
|
||||
(setq use-hard-newlines nil)
|
||||
(setq adaptive-fill-mode t)))
|
||||
|
||||
(prot-emacs-configure
|
||||
;; Arch Linux and AUR package scripts (sh-mode)
|
||||
(add-to-list 'auto-mode-alist '("PKGBUILD" . sh-mode))
|
||||
;; SystemD and other configuration files (conf-mode)
|
||||
(add-to-list 'auto-mode-alist '("\\`dircolors\\'" "\\.\\(service\\|timer\\)\\'" "dunstrc")))
|
||||
|
||||
;;;; Eldoc (Emacs live documentation feedback)
|
||||
(use-package eldoc
|
||||
:ensure nil
|
||||
:hook (prog-mode . eldoc-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(add-hook 'prog-mode-hook #'eldoc-mode)
|
||||
(setq eldoc-idle-delay 1.0)
|
||||
(setq eldoc-message-function #'message)) ; don't use mode line for M-x eval-expression, etc.
|
||||
|
||||
;;;; Eglot (built-in client for the language server protocol)
|
||||
(use-package eglot
|
||||
:ensure nil
|
||||
:functions (eglot-ensure)
|
||||
:commands (eglot)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq eglot-sync-connect nil)
|
||||
(setq eglot-autoshutdown t))
|
||||
|
||||
;;;; Handle performance for very long lines (so-long.el)
|
||||
(use-package so-long
|
||||
:ensure nil
|
||||
:hook (after-init . global-so-long-mode))
|
||||
(prot-emacs-configure
|
||||
(global-so-long-mode 1))
|
||||
|
||||
;;; Markdown (markdown-mode)
|
||||
(use-package markdown-mode
|
||||
:ensure t
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install markdown-mode)
|
||||
(setq markdown-fontify-code-blocks-natively t))
|
||||
|
||||
;;; csv-mode
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install csv-mode))
|
||||
|
||||
;;; SXHKDRC mode (one of my many packages)
|
||||
(use-package sxhkdrc-mode
|
||||
:ensure t
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install sxhkdrc-mode)
|
||||
;; By default, it only applies to the sxhkdrc file, but I have other
|
||||
;; relevant entries as well. I separate my keys into different
|
||||
;; modules and load only what I need.
|
||||
:mode "sxhkdrc_.*")
|
||||
(add-to-list 'auto-mode-alist '("sxhkdrc_.*" . sxhkdrc-mode)))
|
||||
|
||||
;;; Flyspell and prot-spell.el (spell check)
|
||||
(use-package flyspell
|
||||
:ensure nil
|
||||
:bind
|
||||
( :map flyspell-mode-map
|
||||
("C-;" . nil)
|
||||
:map ctl-x-x-map
|
||||
("s" . flyspell-mode)) ; C-x x s
|
||||
:config
|
||||
(setq flyspell-issue-message-flag nil)
|
||||
(setq flyspell-issue-welcome-flag nil)
|
||||
(setq ispell-program-name "aspell")
|
||||
(setq ispell-dictionary "en_GB"))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-autoload
|
||||
(prot-spell-spell-dwim
|
||||
prot-spell-change-dictionary
|
||||
prot-spell-spell-dwim
|
||||
prot-spell-change-dictionary
|
||||
prot-spell-french-punctuation)
|
||||
"prot-spell")
|
||||
|
||||
(use-package prot-spell
|
||||
:ensure nil
|
||||
:bind
|
||||
(("M-$" . prot-spell-spell-dwim)
|
||||
("C-M-$" . prot-spell-change-dictionary))
|
||||
:config
|
||||
(setq prot-spell-dictionaries
|
||||
'(("EN English" . "en")
|
||||
("EL Ελληνικά" . "el")
|
||||
("FR Français" . "fr")
|
||||
("ES Espanõl" . "es")))
|
||||
(prot-emacs-keybind global-map
|
||||
"M-$" #'prot-spell-spell-dwim
|
||||
"C-M-$" #'prot-spell-change-dictionary
|
||||
"M-i" #'prot-spell-spell-dwim ; override `tab-to-tab-stop
|
||||
"C-M-i" #'prot-spell-change-dictionary) ; override `complete-symbol'
|
||||
|
||||
;; Also check prot-spell.el for what I am doing with
|
||||
;; `prot-spell-ispell-display-buffer'. Then refer to the
|
||||
;; `display-buffer-alist' for the relevant entry.
|
||||
(setq ispell-choices-buffer "*ispell-top-choices*"))
|
||||
(with-eval-after-load 'flyspell
|
||||
(define-key flyspell-mode-map (kbd "C-;") nil)
|
||||
(define-key flyspell-mouse-map (kbd "<mouse-3>") #'flyspell-correct-word)
|
||||
(define-key ctl-x-x-map (kbd "s") #'flyspell-mode) ; C-x x s
|
||||
|
||||
(setq flyspell-issue-message-flag nil)
|
||||
(setq flyspell-issue-welcome-flag nil)
|
||||
(setq ispell-program-name "aspell")
|
||||
(setq ispell-dictionary "en_GB")
|
||||
|
||||
(setq prot-spell-dictionaries
|
||||
'(("EN English" . "en")
|
||||
("EL Ελληνικά" . "el")
|
||||
("FR Français" . "fr")
|
||||
("ES Espanõl" . "es")))
|
||||
|
||||
;; Also check prot-spell.el for what I am doing with
|
||||
;; `prot-spell-ispell-display-buffer'. Then refer to the
|
||||
;; `display-buffer-alist' for the relevant entry.
|
||||
(setq ispell-choices-buffer "*ispell-top-choices*")))
|
||||
|
||||
;;; Flymake
|
||||
(use-package flymake
|
||||
:ensure nil
|
||||
:preface
|
||||
(defvar prot/flymake-mode-projects-path
|
||||
(file-name-as-directory (expand-file-name "Projects" "~/Git/"))
|
||||
"Path to my Git projects.")
|
||||
|
||||
(defun prot/flymake-mode-lexical-binding ()
|
||||
(when lexical-binding
|
||||
(prot-emacs-configure
|
||||
(defun prot/flymake-mode-in-my-projects ()
|
||||
"Enable `flymake-mode' when editing a trusted file."
|
||||
(when (and buffer-file-name
|
||||
(not (file-directory-p buffer-file-name))
|
||||
(file-regular-p buffer-file-name)
|
||||
(seq-some
|
||||
(lambda (directory-or-file)
|
||||
(string-prefix-p (expand-file-name directory-or-file) (expand-file-name buffer-file-name)))
|
||||
trusted-content))
|
||||
(flymake-mode 1)))
|
||||
|
||||
(defun prot/flymake-mode-in-my-projects ()
|
||||
(when-let ((file (buffer-file-name))
|
||||
((string-prefix-p prot/flymake-mode-projects-path
|
||||
(expand-file-name file)))
|
||||
((not (file-directory-p file)))
|
||||
((file-regular-p file)))
|
||||
(add-hook 'find-file-hook #'prot/flymake-mode-lexical-binding nil t)))
|
||||
(add-hook 'prog-mode-hook #'prot/flymake-mode-in-my-projects)
|
||||
|
||||
(add-hook 'emacs-lisp-mode-hook #'prot/flymake-mode-in-my-projects)
|
||||
:bind
|
||||
( :map ctl-x-x-map
|
||||
("m" . flymake-mode) ; C-x x m
|
||||
:map flymake-mode-map
|
||||
("C-c ! s" . flymake-start)
|
||||
("C-c ! d" . flymake-show-buffer-diagnostics) ; Emacs28
|
||||
("C-c ! D" . flymake-show-project-diagnostics) ; Emacs28
|
||||
("C-c ! n" . flymake-goto-next-error)
|
||||
("C-c ! p" . flymake-goto-prev-error))
|
||||
:config
|
||||
(setq flymake-fringe-indicator-position 'left-fringe)
|
||||
(setq flymake-suppress-zero-counters t)
|
||||
(setq flymake-no-changes-timeout nil)
|
||||
(setq flymake-start-on-flymake-mode t)
|
||||
(setq flymake-start-on-save-buffer t)
|
||||
(setq flymake-proc-compilation-prevents-syntax-check t)
|
||||
(setq flymake-wrap-around nil)
|
||||
(setq flymake-mode-line-format
|
||||
'("" flymake-mode-line-exception flymake-mode-line-counters))
|
||||
;; NOTE 2023-07-03: `prot-modeline.el' actually defines the counters
|
||||
;; itself and ignores this.
|
||||
(setq flymake-mode-line-counter-format
|
||||
'("" flymake-mode-line-error-counter
|
||||
flymake-mode-line-warning-counter
|
||||
flymake-mode-line-note-counter ""))
|
||||
(setq flymake-show-diagnostics-at-end-of-line nil)) ; Emacs 30
|
||||
(define-key ctl-x-x-map (kbd "m") #'flymake-mode) ; C-x x m
|
||||
|
||||
(with-eval-after-load 'flymake
|
||||
(prot-emacs-keybind flymake-mode-map
|
||||
"C-c ! s" #'flymake-start
|
||||
"C-c ! d" #'flymake-show-buffer-diagnostics ; Emacs 28
|
||||
"C-c ! D" #'flymake-show-project-diagnostics ; Emacs 28
|
||||
"C-c ! n" #'flymake-goto-next-error
|
||||
"C-c ! p" #'flymake-goto-prev-error)
|
||||
|
||||
(setq flymake-fringe-indicator-position 'left-fringe)
|
||||
(setq flymake-suppress-zero-counters t)
|
||||
(setq flymake-no-changes-timeout nil)
|
||||
(setq flymake-start-on-flymake-mode t)
|
||||
(setq flymake-start-on-save-buffer t)
|
||||
(setq flymake-proc-compilation-prevents-syntax-check t)
|
||||
(setq flymake-wrap-around nil)
|
||||
(setq flymake-mode-line-format
|
||||
'("" flymake-mode-line-exception flymake-mode-line-counters))
|
||||
;; NOTE 2023-07-03: `prot-modeline.el' actually defines the counters
|
||||
;; itself and ignores this.
|
||||
(setq flymake-mode-line-counter-format
|
||||
'("" flymake-mode-line-error-counter
|
||||
flymake-mode-line-warning-counter
|
||||
flymake-mode-line-note-counter ""))
|
||||
(setq flymake-inline-diagnostics ; Emacs 32
|
||||
'((current . fancy)
|
||||
(current-line . short)
|
||||
(t . nil))))
|
||||
|
||||
;;; Elisp packaging requirements
|
||||
(use-package package-lint-flymake
|
||||
:ensure t
|
||||
:after flymake
|
||||
:config
|
||||
(add-hook 'flymake-diagnostic-functions #'package-lint-flymake))
|
||||
(prot-emacs-install package-lint-flymake)
|
||||
(package-lint-flymake-setup))
|
||||
|
||||
;;; General configurations for prose/writing
|
||||
|
||||
;;;; `outline' (`outline-mode' and `outline-minor-mode')
|
||||
(use-package outline
|
||||
:ensure nil
|
||||
:bind
|
||||
("<f10>" . outline-minor-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(define-key global-map (kbd "<f10>") #'outline-minor-mode)
|
||||
(setq outline-minor-mode-highlight nil) ; emacs28
|
||||
(setq outline-minor-mode-cycle t) ; emacs28
|
||||
(setq outline-minor-mode-use-buttons nil) ; emacs29---bless you for the nil option!
|
||||
(setq outline-minor-mode-use-margins nil)) ; as above
|
||||
|
||||
;;;; `dictionary'
|
||||
(use-package dictionary
|
||||
:ensure nil
|
||||
:bind ("C-c d" . dictionary-search)
|
||||
:config
|
||||
(setq dictionary-server "dict.org"
|
||||
dictionary-default-popup-strategy "lev" ; read doc string
|
||||
dictionary-create-buttons nil
|
||||
dictionary-use-single-buffer t))
|
||||
(prot-emacs-configure
|
||||
(define-key global-map (kbd "C-c d") #'dictionary-search)
|
||||
(setq dictionary-server "dict.org")
|
||||
(setq dictionary-default-popup-strategy "lev") ; read doc string
|
||||
(setq dictionary-create-buttons nil)
|
||||
(setq dictionary-use-single-buffer t))
|
||||
|
||||
;;; aLtCaPs
|
||||
;; Read the manual: <https://protesilaos.com/emacs/altcaps>.
|
||||
(use-package altcaps
|
||||
:ensure t
|
||||
:bind
|
||||
("C-x C-a" . altcaps-dwim)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install altcaps)
|
||||
(define-key global-map (kbd "C-x C-a") #'altcaps-dwim)
|
||||
;; Force letter casing for certain characters (for legibility).
|
||||
(setq altcaps-force-character-casing
|
||||
'(;; Greek theta
|
||||
(?θ . downcase))))
|
||||
|
||||
;;; Denote (simple note-taking and file-naming)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/denote>.
|
||||
(use-package denote
|
||||
:ensure t
|
||||
:hook
|
||||
;; If you use Markdown or plain text files you want to buttonise
|
||||
;; existing buttons upon visiting the file (Org renders links as
|
||||
;; buttons right away).
|
||||
((find-file . denote-link-buttonize-buffer)
|
||||
|
||||
;; Highlight Denote file names in Dired buffers. Below is the
|
||||
;; generic approach, which is great if you rename files Denote-style
|
||||
;; in lots of places as I do.
|
||||
;;
|
||||
;; If you only want the `denote-dired-mode' in select directories,
|
||||
;; then modify the variable `denote-dired-directories' and use the
|
||||
;; following instead:
|
||||
;;
|
||||
;; (dired-mode . denote-dired-mode-in-directories)
|
||||
(dired-mode . denote-dired-mode))
|
||||
:bind
|
||||
;; Denote DOES NOT define any key bindings. This is for the user to
|
||||
;; decide. For example:
|
||||
( :map global-map
|
||||
("C-c n n" . denote)
|
||||
("C-c n N" . denote-type)
|
||||
("C-c n d" . denote-date)
|
||||
("C-c n z" . denote-signature) ; "zettelkasten" mnemonic
|
||||
("C-c n s" . denote-subdirectory)
|
||||
("C-c n o" . denote-sort-dired) ; "order" mnemonic
|
||||
("C-c n j" . denote-journal-extras-new-entry)
|
||||
("C-c n J" . denote-journal-extras-new-or-existing-entry)
|
||||
;; Note that `denote-rename-file' can work from any context, not
|
||||
;; just Dired buffers. That is why we bind it here to the
|
||||
;; `global-map'.
|
||||
;;
|
||||
;; Also see `denote-rename-file-using-front-matter' further below.
|
||||
("C-c n r" . denote-rename-file)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/denote>. This does
|
||||
;; not include all the useful features of Denote. I have a separate
|
||||
;; private setup for those, as I need to test everything is in order.
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install denote)
|
||||
(add-hook 'text-mode-hook #'denote-fontify-links-mode)
|
||||
(add-hook 'dired-mode-hook #'denote-dired-mode)
|
||||
|
||||
;; If you intend to use Denote with a variety of file types, it is
|
||||
;; easier to bind the link-related commands to the `global-map', as
|
||||
;; shown here. Otherwise follow the same pattern for
|
||||
;; `org-mode-map', `markdown-mode-map', and/or `text-mode-map'.
|
||||
:map text-mode-map
|
||||
("C-c n i" . denote-link) ; "insert" mnemonic
|
||||
("C-c n I" . denote-add-links)
|
||||
("C-c n b" . denote-backlinks)
|
||||
("C-c n f f" . denote-find-link)
|
||||
("C-c n f b" . denote-find-backlink)
|
||||
;; Also see `denote-rename-file' further above.
|
||||
("C-c n R" . denote-rename-file-using-front-matter)
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c n n" #'denote
|
||||
"C-c n N" #'denote-type
|
||||
"C-c n d" #'denote-dired
|
||||
"C-c n g" #'denote-grep
|
||||
"C-c n r" #'denote-rename-file)
|
||||
|
||||
;; I do not bind the Org dynamic blocks, but they are useful:
|
||||
;;
|
||||
;; - `denote-org-extras-dblock-insert-links'
|
||||
;; - `denote-org-extras-dblock-insert-backlinks'
|
||||
;; - `denote-org-extras-dblock-insert-files'
|
||||
;; - `denote-org-extras-dblock-insert-missing-links'
|
||||
(with-eval-after-load 'text-mode
|
||||
(prot-emacs-keybind text-mode-map
|
||||
"C-c n i" #'denote-link ; "insert" mnemonic
|
||||
"C-c n I" #'denote-add-links
|
||||
"C-c n b" #'denote-backlinks
|
||||
"C-c n R" #'denote-rename-file-using-front-matter))
|
||||
|
||||
;; Key bindings specifically for Dired.
|
||||
:map dired-mode-map
|
||||
("C-c C-d C-i" . denote-link-dired-marked-notes)
|
||||
("C-c C-d C-r" . denote-dired-rename-marked-files)
|
||||
("C-c C-d C-k" . denote-dired-rename-marked-files-with-keywords)
|
||||
("C-c C-d C-f" . denote-dired-rename-marked-files-using-front-matter))
|
||||
:config
|
||||
;; Remember to check the doc strings of those variables.
|
||||
(setq denote-directory (expand-file-name "~/Documents/notes/"))
|
||||
(setq denote-file-type 'text) ; Org is the default, set others here like I do
|
||||
;; If you want to have a "controlled vocabulary" of keywords,
|
||||
;; meaning that you only use a predefined set of them, then you want
|
||||
;; `denote-infer-keywords' to be nil and `denote-known-keywords' to
|
||||
;; have the keywords you need.
|
||||
(setq denote-known-keywords '("emacs" "philosophy" "politics" "economics"))
|
||||
(setq denote-infer-keywords t)
|
||||
(setq denote-sort-keywords t)
|
||||
(setq denote-excluded-directories-regexp nil)
|
||||
(setq denote-date-format nil) ; read its doc string
|
||||
(setq denote-rename-no-confirm t)
|
||||
(setq denote-backlinks-show-context nil)
|
||||
(setq denote-rename-buffer-format "[D] %t")
|
||||
(with-eval-after-load 'dired
|
||||
(prot-emacs-keybind dired-mode-map
|
||||
"C-c C-d C-i" #'denote-dired-link-marked-notes
|
||||
"C-c C-d C-r" #'denote-dired-rename-marked-files
|
||||
"C-c C-d C-k" #'denote-dired-rename-marked-files-with-keywords
|
||||
"C-c C-d C-f" #'denote-dired-rename-marked-files-using-front-matter))
|
||||
|
||||
;; Automatically rename Denote buffers when opening them so that
|
||||
;; instead of their long file name they have a literal "[D]"
|
||||
;; followed by the file's title. Read the doc string of
|
||||
;; `denote-rename-buffer-format' for how to modify this.
|
||||
(denote-rename-buffer-mode 1)
|
||||
(with-eval-after-load 'denote
|
||||
(setq denote-directory (expand-file-name "~/Documents/notes/"))
|
||||
(setq denote-file-type 'text) ; Org is the default file type
|
||||
|
||||
(setq denote-journal-extras-directory nil) ; use the `denote-directory'
|
||||
(setq denote-journal-extras-title-format nil) ; always prompt for title
|
||||
(setq denote-journal-extras-keyword "journal")
|
||||
(setq denote-known-keywords '("emacs" "philosophy" "politics"))
|
||||
(setq denote-infer-keywords t)
|
||||
(setq denote-sort-keywords t)
|
||||
(setq denote-excluded-directories-regexp nil)
|
||||
(setq denote-date-format nil)
|
||||
(setq denote-rename-confirmations nil) ; CAREFUL with this if you are not familiar with Denote!
|
||||
(setq denote-backlinks-show-context nil)
|
||||
(setq denote-buffer-name-prefix "[D] ")
|
||||
(setq denote-rename-buffer-format "%D")
|
||||
|
||||
(with-eval-after-load 'org-capture
|
||||
(setq denote-org-capture-specifiers "%l\n%i\n%?")
|
||||
(add-to-list 'org-capture-templates
|
||||
'("n" "New note (with denote.el)" plain
|
||||
(file denote-last-path)
|
||||
#'denote-org-capture
|
||||
:no-save t
|
||||
:immediate-finish nil
|
||||
:kill-buffer t
|
||||
:jump-to-captured t))
|
||||
(denote-rename-buffer-mode 1)
|
||||
|
||||
;; This prompts for TITLE, KEYWORDS, and SUBDIRECTORY
|
||||
(add-to-list 'org-capture-templates
|
||||
'("N" "New note with prompts (with denote.el)" plain
|
||||
(file denote-last-path)
|
||||
(function
|
||||
(lambda ()
|
||||
(denote-org-capture-with-prompts :title :keywords :signature)))
|
||||
:no-save t
|
||||
:immediate-finish nil
|
||||
:kill-buffer t
|
||||
:jump-to-captured t))))
|
||||
;; ;; EXPERIMENT 2025-04-25: This is not the default order, though
|
||||
;; ;; Denote supports any order for its file name components.
|
||||
;; (setq denote-file-name-components-order '(identifier signature keywords title))
|
||||
|
||||
(defun prot/denote-rename-all-to-reorder-components ()
|
||||
"Call `denote-dired-rename-files' without any prompts.
|
||||
In other words, preserve the value of each Denote file name component.
|
||||
|
||||
Use this command if you want to modify the user option
|
||||
`denote-file-name-components-order' and then want your files to
|
||||
retroactively follow that order."
|
||||
(interactive)
|
||||
(let ((denote-prompts nil))
|
||||
(call-interactively 'denote-dired-rename-files)))))
|
||||
|
||||
;;;; Integrate Consult with Denote
|
||||
|
||||
(when prot-emacs-completion-extras
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install consult-denote)
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c n f" #'consult-denote-find
|
||||
"C-c n g" #'consult-denote-grep)
|
||||
(consult-denote-mode 1)))
|
||||
|
||||
;;;; Denote Org extras (denote-org)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install denote-org))
|
||||
|
||||
;;;; Denote Sequence notes or folgezettel (denote-sequence)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install denote-sequence)
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c n s s" #'denote-sequence
|
||||
"C-c n s f" #'denote-sequence-find
|
||||
"C-c n s l" #'denote-sequence-link
|
||||
"C-c n s d" #'denote-sequence-dired
|
||||
"C-c n s r" #'denote-sequence-reparent
|
||||
"C-c n s c" #'denote-sequence-convert)
|
||||
(setq denote-sequence-scheme 'alphanumeric))
|
||||
|
||||
;;;; Denote Markdown extras (denote-markdown)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install denote-markdown))
|
||||
|
||||
;;;; Denote Journal extras (denote-journal)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install denote-journal)
|
||||
|
||||
(add-hook 'calendar-mode-hook #'denote-journal-calendar-mode)
|
||||
|
||||
(defun prot/denote-journal-new-or-existing-entry ()
|
||||
"EXPERIMENTAL Like `denote-journal-new-or-existing-entry' but with no front matter."
|
||||
(interactive)
|
||||
(cl-letf (((symbol-function #'denote--format-front-matter) (lambda (&rest _) ""))
|
||||
(denote-file-type 'text)
|
||||
(denote-journal-title-format ""))
|
||||
(let* ((internal-date (current-time))
|
||||
(files (denote-journal--entry-today internal-date)))
|
||||
(if files
|
||||
(find-file (denote-journal-select-file-prompt files))
|
||||
(call-interactively 'denote-journal-new-entry)))))
|
||||
|
||||
(define-key global-map (kbd "C-c n j") #'prot/denote-journal-new-or-existing-entry)
|
||||
|
||||
(with-eval-after-load 'denote
|
||||
(setq denote-journal-directory (expand-file-name "journal" denote-directory))
|
||||
(setq denote-journal-keyword "journal")
|
||||
(setq denote-journal-title-format 'day-date-month-year)))
|
||||
|
||||
;;;; Denote Silo extras (denote-silo)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install denote-silo)
|
||||
(with-eval-after-load 'denote
|
||||
(setq denote-silo-directories
|
||||
(append (denote-directories)
|
||||
(list "~/Documents/books/" "~/Documents/denote-test-silo/")))))
|
||||
|
||||
;;; Custom extensions for "focus mode" (logos.el)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/logos>.
|
||||
(use-package olivetti
|
||||
:ensure t
|
||||
:commands (olivetti-mode)
|
||||
:config
|
||||
(setq olivetti-body-width 0.7)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install olivetti)
|
||||
(setq-default olivetti-body-width 0.7)
|
||||
(setq olivetti-minimum-body-width 80)
|
||||
(setq olivetti-recall-visual-line-mode-entry-state t))
|
||||
(setq olivetti-recall-visual-line-mode-entry-state t)
|
||||
|
||||
(use-package logos
|
||||
:ensure t
|
||||
:bind
|
||||
(("C-x n n" . logos-narrow-dwim)
|
||||
("C-x ]" . logos-forward-page-dwim)
|
||||
("C-x [" . logos-backward-page-dwim)
|
||||
;; I don't think I ever saw a package bind M-] or M-[...
|
||||
("M-]" . logos-forward-page-dwim)
|
||||
("M-[" . logos-backward-page-dwim)
|
||||
("<f9>" . logos-focus-mode))
|
||||
:config
|
||||
(setq logos-outlines-are-pages t)
|
||||
(setq logos-outline-regexp-alist
|
||||
`((emacs-lisp-mode . ,(format "\\(^;;;+ \\|%s\\)" logos-page-delimiter))
|
||||
(org-mode . ,(format "\\(^\\*+ +\\|^-\\{5\\}$\\|%s\\)" logos-page-delimiter))
|
||||
(markdown-mode . ,(format "\\(^\\#+ +\\|^[*-]\\{5\\}$\\|^\\* \\* \\*$\\|%s\\)" logos-page-delimiter))
|
||||
(conf-toml-mode . "^\\[")))
|
||||
(prot-emacs-install logos)
|
||||
|
||||
;; These apply when `logos-focus-mode' is enabled. Their value is
|
||||
;; buffer-local.
|
||||
(setq-default logos-hide-mode-line t)
|
||||
(setq-default logos-hide-header-line t)
|
||||
(setq-default logos-hide-buffer-boundaries t)
|
||||
(setq-default logos-hide-fringe t)
|
||||
(setq-default logos-variable-pitch t) ; see my `fontaine' configurations
|
||||
(setq-default logos-buffer-read-only nil)
|
||||
(setq-default logos-scroll-lock nil)
|
||||
(setq-default logos-olivetti t)
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x n n" #'logos-narrow-dwim
|
||||
"C-x ]" #'logos-forward-page-dwim
|
||||
"C-x [" #'logos-backward-page-dwim
|
||||
"M-]" #'logos-forward-page-dwim
|
||||
"M-[" #'logos-backward-page-dwim
|
||||
"M-n" #'logos-forward-page-dwim
|
||||
"M-p" #'logos-backward-page-dwim
|
||||
"<f9>" #'logos-focus-mode)
|
||||
|
||||
(add-hook 'enable-theme-functions #'logos-update-fringe-in-buffers)
|
||||
(with-eval-after-load 'logos
|
||||
(setq logos-outlines-are-pages t)
|
||||
(setq logos-outline-regexp-alist
|
||||
`((emacs-lisp-mode . ,(format "\\(^;;;+ \\|%s\\)" logos-page-delimiter))
|
||||
(org-mode . ,(format "\\(^\\*+ +\\|^-\\{5\\}$\\|%s\\)" logos-page-delimiter))
|
||||
(markdown-mode . ,(format "\\(^\\#+ +\\|^[*-]\\{5\\}$\\|^\\* \\* \\*$\\|%s\\)" logos-page-delimiter))
|
||||
(conf-toml-mode . "^\\[")))
|
||||
|
||||
;; These apply when `logos-focus-mode' is enabled. Their value is
|
||||
;; buffer-local.
|
||||
(setq-default logos-hide-mode-line t)
|
||||
(setq-default logos-hide-header-line t)
|
||||
(setq-default logos-hide-buffer-boundaries t)
|
||||
(setq-default logos-hide-fringe t)
|
||||
(setq-default logos-variable-pitch t) ; see my `fontaine' configurations
|
||||
(setq-default logos-buffer-read-only nil)
|
||||
(setq-default logos-scroll-lock nil)
|
||||
(setq-default logos-olivetti t)
|
||||
|
||||
(add-hook 'enable-theme-functions #'logos-update-fringe-in-buffers)
|
||||
|
||||
;;;; Extra tweaks
|
||||
;; place point at the top when changing pages, but not in `prog-mode'
|
||||
(defun prot/logos--recenter-top ()
|
||||
"Use `recenter' to reposition the view at the top."
|
||||
(unless (derived-mode-p 'prog-mode)
|
||||
(recenter 1))) ; Use 0 for the absolute top
|
||||
;; place point at the top when changing pages, but not in `prog-mode'
|
||||
(defun prot/logos-recenter-top ()
|
||||
"Use `recenter' to reposition the view at the top."
|
||||
(unless (derived-mode-p 'prog-mode)
|
||||
(recenter 1))) ; Use 0 for the absolute top
|
||||
|
||||
(add-hook 'logos-page-motion-hook #'prot/logos--recenter-top))
|
||||
(add-hook 'logos-page-motion-hook #'prot/logos-recenter-top)))
|
||||
|
||||
(provide 'prot-emacs-langs)
|
||||
|
|
|
|||
|
|
@ -1,21 +1,61 @@
|
|||
;;; Minibuffer and Completions in Tandem or Minibuffer Confines Transcended (mct)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/mct>.
|
||||
(use-package mct
|
||||
:ensure t
|
||||
:hook (after-init . mct-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install mct)
|
||||
|
||||
(setq mct-hide-completion-mode-line t)
|
||||
(setq mct-completing-read-multiple-indicator t)
|
||||
;; The blocklist and passlist accept either commands/functions or
|
||||
;; completion categories.
|
||||
(setq mct-completion-blocklist '(notmuch-mua-new-mail notmuch-mua-prompt-for-sender))
|
||||
(setq mct-completion-passlist
|
||||
'( consult-buffer consult-location embark-keybinding
|
||||
imenu prot-search-outline select-frame-by-name))
|
||||
'(;; Some commands
|
||||
prot-search-outline
|
||||
select-frame-by-name
|
||||
Info-goto-node
|
||||
Info-index
|
||||
Info-menu
|
||||
vc-retrieve-tag
|
||||
;; Some completion categories
|
||||
consult-buffer
|
||||
consult-location
|
||||
embark-keybinding
|
||||
imenu
|
||||
file
|
||||
project-file
|
||||
buffer
|
||||
kill-ring))
|
||||
(setq mct-remove-shadowed-file-names t)
|
||||
(setq mct-completion-window-size (cons #'mct-frame-height-third 1))
|
||||
(setq mct-persist-dynamic-completion nil)
|
||||
(setq mct-live-completion 'visible)
|
||||
(setq completions-sort #'mct-sort-multi-category))
|
||||
|
||||
;; This is the default value but I am keeping it here in case I
|
||||
;; choose to modify it.
|
||||
(setq mct-sort-by-command-or-category
|
||||
'((file . mct-sort-by-directory-then-by-file)
|
||||
((magit-checkout vc-retrieve-tag) . mct-sort-by-alpha-then-by-length)
|
||||
((kill-ring imenu consult-location Info-goto-node Info-index Info-menu) . nil) ; no sorting
|
||||
(t . mct-sort-by-history)))
|
||||
|
||||
;; Adaptation of `icomplete-fido-backward-updir'.
|
||||
(defun prot/mct-backward-updir ()
|
||||
"Delete char before point or go up a directory."
|
||||
(interactive nil mct-mode)
|
||||
(cond
|
||||
((and (eq (char-before) ?/)
|
||||
(eq (mct--completion-category) 'file))
|
||||
(when (string-equal (minibuffer-contents) "~/")
|
||||
(delete-minibuffer-contents)
|
||||
(insert (expand-file-name "~/"))
|
||||
(goto-char (line-end-position)))
|
||||
(save-excursion
|
||||
(goto-char (1- (point)))
|
||||
(when (search-backward "/" (minibuffer-prompt-end) t)
|
||||
(delete-region (1+ (point)) (point-max)))))
|
||||
(t (call-interactively 'backward-delete-char))))
|
||||
|
||||
(define-key minibuffer-local-filename-completion-map (kbd "DEL") #'prot/mct-backward-updir)
|
||||
|
||||
(mct-mode 1))
|
||||
|
||||
(provide 'prot-emacs-mct)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
;;; Mode line
|
||||
(use-package prot-modeline
|
||||
:ensure nil
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(require 'prot-modeline)
|
||||
(setq mode-line-compact nil) ; Emacs 28
|
||||
(setq mode-line-right-align-edge 'right-margin) ; Emacs 30
|
||||
(setq prot-modeline-show-frame-name (alist-get 'undecorated initial-frame-alist))
|
||||
|
||||
(setq-default mode-line-format
|
||||
'("%e"
|
||||
prot-modeline-kbd-macro
|
||||
|
|
@ -17,6 +18,8 @@
|
|||
prot-modeline-major-mode
|
||||
prot-modeline-process
|
||||
" "
|
||||
prot-modeline-frame-name
|
||||
" "
|
||||
prot-modeline-vc-branch
|
||||
" "
|
||||
prot-modeline-eglot
|
||||
|
|
@ -24,15 +27,55 @@
|
|||
prot-modeline-flymake
|
||||
" "
|
||||
mode-line-format-right-align ; Emacs 30
|
||||
prot-modeline-which-function-indicator
|
||||
prot-modeline-notmuch-indicator
|
||||
" "
|
||||
prot-modeline-misc-info))
|
||||
|
||||
(when prot-emacs-load-theme-family
|
||||
(cond
|
||||
((memq prot-emacs-load-theme-family '(modus ef standard))
|
||||
(defun prot/modeline-set-faces ()
|
||||
(modus-themes-with-colors
|
||||
(custom-set-faces
|
||||
`(prot-modeline-indicator-red ((,c :inherit bold :foreground ,red)))
|
||||
`(prot-modeline-indicator-green ((,c :inherit bold :foreground ,green)))
|
||||
`(prot-modeline-indicator-yellow ((,c :inherit bold :foreground ,yellow)))
|
||||
`(prot-modeline-indicator-blue ((,c :inherit bold :foreground ,blue)))
|
||||
`(prot-modeline-indicator-magenta ((,c :inherit bold :foreground ,magenta)))
|
||||
`(prot-modeline-indicator-cyan ((,c :inherit bold :foreground ,cyan)))
|
||||
`(prot-modeline-indicator-red-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-red-intense :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-green-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-green-intense :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-yellow-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-yellow-intense :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-blue-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-blue-intense :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-magenta-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-magenta-intense :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-cyan-bg ((,c :inherit (bold prot-modeline-indicator-button) :background ,bg-cyan-intense :foreground ,fg-main))))))
|
||||
(add-hook 'modus-themes-after-load-theme-hook #'prot/modeline-set-faces))
|
||||
((eq prot-emacs-load-theme-family 'doric)
|
||||
(defun prot/modeline-set-faces ()
|
||||
(doric-themes-with-colors
|
||||
(custom-set-faces
|
||||
`(prot-modeline-indicator-red ((t :inherit bold :foreground ,fg-red)))
|
||||
`(prot-modeline-indicator-green ((t :inherit bold :foreground ,fg-green)))
|
||||
`(prot-modeline-indicator-yellow ((t :inherit bold :foreground ,fg-yellow)))
|
||||
`(prot-modeline-indicator-blue ((t :inherit bold :foreground ,fg-blue)))
|
||||
`(prot-modeline-indicator-magenta ((t :inherit bold :foreground ,fg-magenta)))
|
||||
`(prot-modeline-indicator-cyan ((t :inherit bold :foreground ,fg-cyan)))
|
||||
`(prot-modeline-indicator-red-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-red :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-green-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-green :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-yellow-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-yellow :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-blue-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-blue :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-magenta-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-magenta :foreground ,fg-main)))
|
||||
`(prot-modeline-indicator-cyan-bg ((t :inherit (bold prot-modeline-indicator-button) :background ,bg-cyan :foreground ,fg-main))))))
|
||||
(add-hook 'doric-themes-after-load-theme-hook #'prot/modeline-set-faces)))
|
||||
|
||||
(prot/modeline-set-faces))
|
||||
|
||||
(with-eval-after-load 'spacious-padding
|
||||
(defun prot/modeline-spacious-indicators ()
|
||||
"Set box attribute to `'prot-modeline-indicator-button' if spacious-padding is enabled."
|
||||
(if (bound-and-true-p spacious-padding-mode)
|
||||
(set-face-attribute 'prot-modeline-indicator-button nil :box t)
|
||||
(set-face-attribute 'prot-modeline-indicator-button nil :box '(:style flat-button))
|
||||
(set-face-attribute 'prot-modeline-indicator-button nil :box 'unspecified)))
|
||||
|
||||
;; Run it at startup and then afterwards whenever
|
||||
|
|
@ -41,22 +84,27 @@
|
|||
|
||||
(add-hook 'spacious-padding-mode-hook #'prot/modeline-spacious-indicators)))
|
||||
|
||||
;;; Keycast mode
|
||||
(use-package keycast
|
||||
:ensure t
|
||||
:after prot-modeline
|
||||
:commands (keycast-mode-line-mode keycast-header-line-mode keycast-tab-bar-mode keycast-log-mode)
|
||||
:init
|
||||
(setq keycast-mode-line-format "%2s%k%c%R")
|
||||
(setq keycast-mode-line-insert-after 'prot-modeline-vc-branch)
|
||||
(setq keycast-mode-line-window-predicate 'mode-line-window-selected-p)
|
||||
(setq keycast-mode-line-remove-tail-elements nil)
|
||||
:config
|
||||
(dolist (input '(self-insert-command org-self-insert-command))
|
||||
(add-to-list 'keycast-substitute-alist `(,input "." "Typing…")))
|
||||
;;; Show the name of the current definition or heading for context (`which-function-mode')
|
||||
(prot-emacs-configure
|
||||
(setq which-func-modes '(prog-mode org-mode))
|
||||
;; NOTE 2025-10-26: I handle the indicator on my own via `prot-modeline-which-function-indicator'.
|
||||
(setq which-func-display 'mode) ; Emacs 30
|
||||
(setq which-func-unknown "")
|
||||
(which-function-mode 1))
|
||||
|
||||
(dolist (event '( mouse-event-p mouse-movement-p mwheel-scroll handle-select-window
|
||||
mouse-set-point mouse-drag-region))
|
||||
(add-to-list 'keycast-substitute-alist `(,event nil))))
|
||||
;;; Keycast mode
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install keycast)
|
||||
(with-eval-after-load 'prot-modeline
|
||||
(setq keycast-mode-line-format "%2s%k%c%R")
|
||||
(setq keycast-mode-line-insert-after 'prot-modeline-vc-branch)
|
||||
(setq keycast-mode-line-window-predicate 'mode-line-window-selected-p)
|
||||
(setq keycast-mode-line-remove-tail-elements nil))
|
||||
(with-eval-after-load 'keycast
|
||||
(dolist (input '(self-insert-command org-self-insert-command))
|
||||
(add-to-list 'keycast-substitute-alist `(,input "." "Typing…")))
|
||||
|
||||
(dolist (event '("<mouse-event>" "<mouse-movement>" "<mouse-2>" "<drag-mouse-1>" "<wheel-up>" "<wheel-down>" "<double-wheel-up>" "<double-wheel-down>" "<triple-wheel-up>" "<triple-wheel-down>" "<wheel-left>" "<wheel-right>" handle-select-window mouse-set-point mouse-drag-region))
|
||||
(add-to-list 'keycast-substitute-alist `(,event nil nil)))))
|
||||
|
||||
(provide 'prot-emacs-modeline)
|
||||
|
|
|
|||
|
|
@ -2,86 +2,69 @@
|
|||
|
||||
;; The themes are highly customisable. Read the manual:
|
||||
;; <https://protesilaos.com/emacs/modus-themes>.
|
||||
(use-package modus-themes
|
||||
:ensure t
|
||||
:demand t
|
||||
:bind (("<f5>" . modus-themes-toggle)
|
||||
("C-<f5>" . modus-themes-select))
|
||||
:config
|
||||
(setq modus-themes-custom-auto-reload nil
|
||||
modus-themes-to-toggle '(modus-operandi modus-vivendi)
|
||||
;; modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted)
|
||||
;; modus-themes-to-toggle '(modus-operandi-deuteranopia modus-vivendi-deuteranopia)
|
||||
;; modus-themes-to-toggle '(modus-operandi-tritanopia modus-vivendi-tritanopia)
|
||||
modus-themes-mixed-fonts t
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install modus-themes)
|
||||
;; Starting with version 5.0.0 of the `modus-themes', other packages
|
||||
;; can be built on top to provide their own "Modus" derivatives.
|
||||
;; For example, this is what I do with my `ef-themes' and
|
||||
;; `standard-themes' (starting with versions 2.0.0 and 3.0.0,
|
||||
;; respectively).
|
||||
;;
|
||||
;; The `modus-themes-include-derivatives-mode' makes all Modus
|
||||
;; commands that act on a theme consider all such derivatives, if
|
||||
;; their respective packages are available and have been loaded.
|
||||
;;
|
||||
;; Note that those packages can even completely take over from the
|
||||
;; Modus themes such that, for example, `modus-themes-rotate' only
|
||||
;; goes through the Ef themes (to this end, the Ef themes provide
|
||||
;; the `ef-themes-take-over-modus-themes-mode' and the Standard
|
||||
;; themes have the `standard-themes-take-over-modus-themes-mode'
|
||||
;; equivalent).
|
||||
;;
|
||||
;; If you only care about the Modus themes, then (i) you do not need
|
||||
;; to enable the `modus-themes-include-derivatives-mode' and (ii) do
|
||||
;; not install and activate those other theme packages.
|
||||
(modus-themes-include-derivatives-mode 1)
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'modus-themes-rotate
|
||||
"C-<f5>" #'modus-themes-select
|
||||
"M-<f5>" #'modus-themes-load-random)
|
||||
|
||||
(setq modus-themes-mixed-fonts t
|
||||
modus-themes-variable-pitch-ui t
|
||||
modus-themes-italic-constructs t
|
||||
modus-themes-bold-constructs nil
|
||||
modus-themes-completions '((t . (extrabold)))
|
||||
modus-themes-prompts '(extrabold)
|
||||
modus-themes-bold-constructs t
|
||||
modus-themes-completions '((t . (bold)))
|
||||
modus-themes-prompts '(bold)
|
||||
modus-themes-headings
|
||||
'((agenda-structure . (variable-pitch light 2.2))
|
||||
(agenda-date . (variable-pitch regular 1.3))
|
||||
(t . (regular 1.15))))
|
||||
|
||||
(setq modus-themes-common-palette-overrides nil)
|
||||
;; '((bg-mode-line-active bg-cyan-subtle)
|
||||
;; (keybind yellow-warmer)))
|
||||
|
||||
(if (prot-emacs-theme-environment-dark-p)
|
||||
(modus-themes-load-theme (cadr modus-themes-to-toggle))
|
||||
(modus-themes-load-theme (car modus-themes-to-toggle))))
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
(modus-themes-load-random 'dark)
|
||||
(modus-themes-load-random 'light)))
|
||||
|
||||
;; NOTE: For testing purposes
|
||||
(prot-emacs-comment
|
||||
(:eval nil)
|
||||
(progn
|
||||
(mapc #'disable-theme custom-enabled-themes)
|
||||
|
||||
(add-to-list 'load-path "/home/prot/Git/Projects/modus-themes/")
|
||||
(add-to-list 'load-path "~/Git/Projects/modus-themes/")
|
||||
|
||||
(require 'modus-themes)
|
||||
(require 'modus-themes)
|
||||
|
||||
(setq modus-themes-custom-auto-reload nil
|
||||
modus-themes-to-toggle '(modus-operandi modus-vivendi)
|
||||
;; modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted)
|
||||
;; modus-themes-to-toggle '(modus-operandi-deuteranopia modus-vivendi-deuteranopia)
|
||||
;; modus-themes-to-toggle '(modus-operandi-tritanopia modus-vivendi-tritanopia)
|
||||
modus-themes-mixed-fonts t
|
||||
modus-themes-variable-pitch-ui nil
|
||||
modus-themes-italic-constructs t
|
||||
modus-themes-bold-constructs t
|
||||
modus-themes-completions '((t . (extrabold)))
|
||||
modus-themes-prompts nil
|
||||
modus-themes-headings
|
||||
'((agenda-structure . (variable-pitch light 2.2))
|
||||
(agenda-date . (variable-pitch regular 1.3))
|
||||
(t . (regular 1.15))))
|
||||
(modus-themes-include-derivatives-mode 1)
|
||||
|
||||
;; (setq modus-themes-common-palette-overrides nil)
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'modus-themes-rotate
|
||||
"C-<f5>" #'modus-themes-select
|
||||
"M-<f5>" #'modus-themes-load-random)
|
||||
|
||||
(setq modus-themes-common-palette-overrides
|
||||
`((fringe unspecified)
|
||||
;; (bg-mode-line-active bg-lavender)
|
||||
;; (border-mode-line-active unspecified)
|
||||
;; (border-mode-line-inactive unspecified)
|
||||
(bg-line-number-active bg-hl-line)
|
||||
(bg-line-number-inactive unspecified)
|
||||
(fg-line-number-active fg-main)
|
||||
;; ,@modus-themes-preset-overrides-warmer
|
||||
))
|
||||
|
||||
;; ;; Make the active mode line have a pseudo 3D effect (this assumes
|
||||
;; ;; you are using the default mode line and not an extra package).
|
||||
;; (custom-set-faces
|
||||
;; '(mode-line ((t :box (:style unspecified)))))
|
||||
|
||||
(if (prot-emacs-theme-environment-dark-p)
|
||||
(modus-themes-load-theme (cadr modus-themes-to-toggle))
|
||||
(modus-themes-load-theme (car modus-themes-to-toggle)))
|
||||
|
||||
;; Also check `modus-themes-select'. To list the palette's colours,
|
||||
;; use `modus-themes-list-colors', `modus-themes-list-colors-current'.
|
||||
(define-key global-map (kbd "<f5>") #'modus-themes-toggle)))
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
(modus-themes-load-random 'dark)
|
||||
(modus-themes-load-random 'light)))
|
||||
|
||||
(provide 'prot-emacs-modus-themes)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
;; This is deprecated code as I stopped using mu4e a long time ago. I
|
||||
;; keep it here for when I need to test it for my themes (`ef-themes',
|
||||
;; `modus-themes', `standard-themes').
|
||||
;; `modus-themes', `standard-themes', `doric-themes').
|
||||
|
||||
(use-package mu4e
|
||||
:ensure nil
|
||||
;; This is an exception because I install it from the system
|
||||
;; distribution's package archives (depends on non-Emacs code)
|
||||
:load-path "/usr/share/emacs/site-lisp/elpa/mu4e-1.8.14"
|
||||
:load-path "/usr/share/emacs/site-lisp/elpa-src/mu4e-1.8.14/"
|
||||
:config
|
||||
(setq mu4e-maildir "~/.20240226-mail-mu4e")
|
||||
(setq mu4e-attachment-dir (expand-file-name "~/Downloads/mail-attachments/"))
|
||||
|
|
@ -147,7 +147,7 @@
|
|||
(setq mu4e-compose-complete-addresses nil
|
||||
mu4e-compose-complete-only-personal t)
|
||||
|
||||
(setq mu4e-compose-signature "Protesilaos Stavrou\nprotesilaos.com\n")
|
||||
(setq mu4e-compose-signature "Protesilaos\nprotesilaos.com\n")
|
||||
(setq mu4e-compose-signature-auto-include t)
|
||||
|
||||
(setq mu4e-context-policy 'pick-first
|
||||
|
|
|
|||
|
|
@ -3,31 +3,24 @@
|
|||
;; I install notmuch from the distro's repos because the CLI program is
|
||||
;; not dependent on Emacs. Though the package also includes notmuch.el
|
||||
;; which is what we use here (they are maintained by the same people).
|
||||
(use-package notmuch
|
||||
:load-path "/usr/share/emacs/site-lisp/"
|
||||
:defer t
|
||||
:commands (notmuch notmuch-mua-new-mail))
|
||||
(prot-emacs-configure
|
||||
(add-to-list 'load-path "/usr/share/emacs/site-lisp/"))
|
||||
|
||||
;;; Account settings
|
||||
(use-package notmuch
|
||||
:defer t
|
||||
:config
|
||||
(let ((prv (prot-common-auth-get-field "prv" :user))
|
||||
(pub (prot-common-auth-get-field "pub" :user))
|
||||
(inf (prot-common-auth-get-field "inf" :user)))
|
||||
(setq notmuch-identities
|
||||
(mapcar (lambda (str)
|
||||
(format "%s <%s>" user-full-name str))
|
||||
(list prv pub inf))
|
||||
notmuch-fcc-dirs
|
||||
`((,prv . "prv/Sent")
|
||||
(,inf . "inf/Sent")
|
||||
(,pub . "pub/Sent")))))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'prot-common
|
||||
(let ((prv (prot-common-auth-get-field "prv-gandi" :user))
|
||||
(pub (prot-common-auth-get-field "pub-gandi" :user))
|
||||
(inf (prot-common-auth-get-field "inf-gandi" :user))
|
||||
(box (prot-common-auth-get-field "prot-gandi" :user)))
|
||||
(setq notmuch-identities
|
||||
(mapcar (lambda (str)
|
||||
(format "%s <%s>" user-full-name str))
|
||||
(list prv pub inf box))
|
||||
notmuch-fcc-dirs `((".*" . "gandi/Sent"))))))
|
||||
|
||||
;;;; General UI
|
||||
(use-package notmuch
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq notmuch-show-logo nil
|
||||
notmuch-column-control 1.0
|
||||
notmuch-hello-auto-refresh t
|
||||
|
|
@ -37,9 +30,7 @@
|
|||
notmuch-show-all-tags-list t))
|
||||
|
||||
;;;; Search
|
||||
(use-package notmuch
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq notmuch-search-oldest-first nil)
|
||||
(setq notmuch-search-result-format
|
||||
'(("date" . "%12s ")
|
||||
|
|
@ -74,12 +65,11 @@
|
|||
:query "tag:inbox"
|
||||
:sort-order newest-first
|
||||
:key ,(kbd "i"))
|
||||
( :name "💬 unread (inbox)"
|
||||
( :name "💬 all unread (inbox)"
|
||||
:query "tag:unread and tag:inbox"
|
||||
:sort-order newest-first
|
||||
:key ,(kbd "u"))
|
||||
;; My packages
|
||||
( :name "🗂️ unread packages"
|
||||
( :name "🛠️ unread packages"
|
||||
:query "tag:unread and tag:package"
|
||||
:sort-order newest-first
|
||||
:key ,(kbd "p"))
|
||||
|
|
@ -87,24 +77,10 @@
|
|||
( :name "🏆 unread coaching"
|
||||
:query "tag:unread and tag:coach"
|
||||
:sort-order newest-first
|
||||
:key ,(kbd "c"))
|
||||
;; Emacs
|
||||
( :name "🔨 emacs-devel"
|
||||
:query "(from:emacs-devel@gnu.org or to:emacs-devel@gnu.org) not tag:archived"
|
||||
:sort-order newest-first
|
||||
:key ,(kbd "e d"))
|
||||
( :name "🦄 emacs-orgmode"
|
||||
:query "(from:emacs-orgmode@gnu.org or to:emacs-orgmode@gnu.org) not tag:archived"
|
||||
:sort-order newest-first
|
||||
:key ,(kbd "e o"))
|
||||
( :name "🐛 emacs-bugs"
|
||||
:query "'to:\"/*@debbugs.gnu.org*/\"' not tag:archived"
|
||||
:sort-order newest-first :key ,(kbd "e b")))))
|
||||
:key ,(kbd "c")))))
|
||||
|
||||
;;;; Tags
|
||||
(use-package notmuch
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq notmuch-archive-tags nil ; I do not archive email
|
||||
notmuch-message-replied-tags '("+replied")
|
||||
notmuch-message-forwarded-tags '("+forwarded")
|
||||
|
|
@ -133,12 +109,11 @@
|
|||
(concat "🏷️" tag)))))
|
||||
|
||||
;;;; Email composition
|
||||
(use-package notmuch
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq notmuch-mua-compose-in 'current-window)
|
||||
(setq notmuch-mua-hidden-headers nil)
|
||||
(setq notmuch-address-command 'internal) ; NOTE 2024-01-09: I am not using this and must review it.
|
||||
(setq notmuch-address-command 'internal)
|
||||
(setq notmuch-address-use-company nil)
|
||||
(setq notmuch-always-prompt-for-sender t)
|
||||
(setq notmuch-mua-cite-function 'message-cite-original-without-signature)
|
||||
(setq notmuch-mua-reply-insert-header-p-function 'notmuch-show-reply-insert-header-p-never)
|
||||
|
|
@ -149,12 +124,31 @@
|
|||
(setq notmuch-mua-attachment-regexp ; see `notmuch-mua-send-hook'
|
||||
(concat "\\b\\(attache\?ment\\|attached\\|attach\\|"
|
||||
"pi[èe]ce\s+jointe?\\|"
|
||||
"συνημμ[εέ]νο\\|επισυν[αά]πτω\\)\\b")))
|
||||
"συνημμ[εέ]νο\\|επισυν[αά]πτω\\)\\b"))
|
||||
|
||||
(with-eval-after-load 'message
|
||||
(defun prot-notmuch-message-tab ()
|
||||
"Override for `message-tab' to enforce header line check.
|
||||
More specifically, perform address completion when on a relevant header
|
||||
line, because `message-tab' sometimes (not sure when/how) fails to do
|
||||
that and instead tries to complete against dictionary entries."
|
||||
(interactive nil message-mode)
|
||||
(cond
|
||||
((save-excursion
|
||||
(goto-char (line-beginning-position))
|
||||
(looking-at notmuch-address-completion-headers-regexp))
|
||||
(notmuch-address-expand-name)
|
||||
;; Completion was performed; nothing else to do.
|
||||
nil)
|
||||
(message-tab-body-function (funcall message-tab-body-function))
|
||||
(t (funcall (or (lookup-key text-mode-map "\t")
|
||||
(lookup-key global-map "\t")
|
||||
'indent-relative)))))
|
||||
|
||||
(advice-add #'message-tab :override #'prot-notmuch-message-tab)))
|
||||
|
||||
;;;; Reading messages
|
||||
(use-package notmuch
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq notmuch-show-relative-dates t)
|
||||
(setq notmuch-show-all-multipart/alternative-parts nil)
|
||||
(setq notmuch-show-indent-messages-width 0)
|
||||
|
|
@ -171,87 +165,100 @@
|
|||
notmuch-wash-citation-lines-suffix count)))
|
||||
|
||||
;;;; Hooks and key bindings
|
||||
(use-package notmuch
|
||||
:hook
|
||||
(notmuch-mua-send . notmuch-mua-attachment-check)
|
||||
(notmuch-show . (lambda () (setq-local header-line-format nil)))
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(add-hook 'notmuch-mua-send-hook #'notmuch-mua-attachment-check) ; also see `notmuch-mua-attachment-regexp'
|
||||
(add-hook 'notmuch-show-hook (lambda () (setq-local header-line-format nil)))
|
||||
(remove-hook 'notmuch-show-hook #'notmuch-show-turn-on-visual-line-mode)
|
||||
(remove-hook 'notmuch-search-hook #'notmuch-hl-line-mode) ; Check my `lin' package
|
||||
:bind
|
||||
( :map global-map
|
||||
("C-c m" . notmuch)
|
||||
("C-x m" . notmuch-mua-new-mail) ; override `compose-mail'
|
||||
:map notmuch-search-mode-map ; I normally don't use the tree view, otherwise check `notmuch-tree-mode-map'
|
||||
("/" . notmuch-search-filter) ; alias for l
|
||||
("r" . notmuch-search-reply-to-thread) ; easier to reply to all by default
|
||||
("R" . notmuch-search-reply-to-thread-sender)
|
||||
:map notmuch-show-mode-map
|
||||
("r" . notmuch-show-reply) ; easier to reply to all by default
|
||||
("R" . notmuch-show-reply-sender)
|
||||
:map notmuch-hello-mode-map
|
||||
("C-<tab>" . nil)))
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c m" #'notmuch
|
||||
"C-x m" #'notmuch-mua-new-mail) ; override `compose-mail'
|
||||
|
||||
(with-eval-after-load 'notmuch
|
||||
(prot-emacs-keybind notmuch-search-mode-map ; I normally don't use the tree view, otherwise check `notmuch-tree-mode-map'
|
||||
"a" nil ; the default is too easy to hit accidentally and I do not archive stuff
|
||||
"A" nil
|
||||
"/" #'notmuch-search-filter ; alias for l
|
||||
"r" #'notmuch-search-reply-to-thread ; easier to reply to all by default
|
||||
"R" #'notmuch-search-reply-to-thread-sender)
|
||||
|
||||
(prot-emacs-keybind notmuch-show-mode-map
|
||||
"a" nil ; the default is too easy to hit accidentally and I do not archive stuff
|
||||
"A" nil
|
||||
"r" #'notmuch-show-reply ; easier to reply to all by default
|
||||
"R" #'notmuch-show-reply-sender)
|
||||
|
||||
(define-key notmuch-hello-mode-map (kbd "C-<tab>") nil)))
|
||||
|
||||
;;; My own tweaks for notmuch (prot-notmuch.el)
|
||||
(use-package prot-notmuch
|
||||
:ensure nil
|
||||
:after notmuch
|
||||
:bind
|
||||
( :map notmuch-search-mode-map
|
||||
("a" . nil) ; the default is too easy to hit accidentally
|
||||
("A" . nil)
|
||||
("D" . prot-notmuch-search-delete-thread)
|
||||
("S" . prot-notmuch-search-spam-thread)
|
||||
("g" . prot-notmuch-refresh-buffer)
|
||||
:map notmuch-show-mode-map
|
||||
("a" . nil) ; the default is too easy to hit accidentally
|
||||
("A" . nil)
|
||||
("D" . prot-notmuch-show-delete-message)
|
||||
("S" . prot-notmuch-show-spam-message)
|
||||
:map notmuch-show-stash-map
|
||||
("S" . prot-notmuch-stash-sourcehut-link))
|
||||
:config
|
||||
;; Those are for the actions that are available after pressing 'k'
|
||||
;; (`notmuch-tag-jump'). For direct actions, refer to the key
|
||||
;; bindings below.
|
||||
(setq notmuch-tagging-keys
|
||||
`((,(kbd "d") prot-notmuch-mark-delete-tags "💥 Mark for deletion")
|
||||
(,(kbd "f") prot-notmuch-mark-flag-tags "🚩 Flag as important")
|
||||
(,(kbd "s") prot-notmuch-mark-spam-tags "🔥 Mark as spam")
|
||||
(,(kbd "r") ("-unread") "👁️🗨️ Mark as read")
|
||||
(,(kbd "u") ("+unread") "🗨️ Mark as unread")))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'notmuch
|
||||
(require 'prot-notmuch)
|
||||
|
||||
;; These emoji are purely cosmetic. The tag remains the same: I
|
||||
;; would not like to input emoji for searching.
|
||||
(add-to-list 'notmuch-tag-formats '("encrypted" (concat tag "🔒")))
|
||||
(add-to-list 'notmuch-tag-formats '("attachment" (concat tag "📎")))
|
||||
(add-to-list 'notmuch-tag-formats '("coach" (concat tag "🏆")))
|
||||
(add-to-list 'notmuch-tag-formats '("package" (concat tag "🗂️"))))
|
||||
(prot-emacs-keybind notmuch-search-mode-map
|
||||
"D" #'prot-notmuch-search-delete-thread
|
||||
"S" #'prot-notmuch-search-spam-thread
|
||||
"g" #'prot-notmuch-refresh-buffer)
|
||||
|
||||
(prot-emacs-keybind notmuch-show-mode-map
|
||||
"D" #'prot-notmuch-show-delete-message
|
||||
"S" #'prot-notmuch-show-spam-message)
|
||||
|
||||
(define-key notmuch-show-stash-map (kbd "S") #'prot-notmuch-stash-sourcehut-link)
|
||||
|
||||
;; Those are for the actions that are available after pressing 'k'
|
||||
;; (`notmuch-tag-jump'). For direct actions, refer to the key
|
||||
;; bindings below.
|
||||
(setq notmuch-tagging-keys
|
||||
`((,(kbd "d") prot-notmuch-mark-delete-tags "💥 Mark for deletion")
|
||||
(,(kbd "f") prot-notmuch-mark-flag-tags "🚩 Flag as important")
|
||||
(,(kbd "s") prot-notmuch-mark-spam-tags "🔥 Mark as spam")
|
||||
(,(kbd "r") ("-unread") "👁️🗨️ Mark as read")
|
||||
(,(kbd "u") ("+unread") "🗨️ Mark as unread")))
|
||||
|
||||
;; These emoji are purely cosmetic. The tag remains the same: I
|
||||
;; would not like to input emoji for searching.
|
||||
(add-to-list 'notmuch-tag-formats '("encrypted" (concat tag "🔒")))
|
||||
(add-to-list 'notmuch-tag-formats '("attachment" (concat tag "📎")))
|
||||
(add-to-list 'notmuch-tag-formats '("coach" (concat tag "🏆")))
|
||||
(add-to-list 'notmuch-tag-formats '("package" (concat tag "🗂️")))))
|
||||
|
||||
;;; Glue code for notmuch and org-link (ol-notmuch.el)
|
||||
(use-package ol-notmuch
|
||||
:ensure t
|
||||
:after notmuch)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install ol-notmuch)
|
||||
(with-eval-after-load 'notmuch
|
||||
(require 'ol-notmuch)))
|
||||
|
||||
;;; notmuch-indicator (another package of mine)
|
||||
(use-package notmuch-indicator
|
||||
:ensure t
|
||||
:after notmuch
|
||||
:config
|
||||
(setq notmuch-indicator-args
|
||||
'((:terms "tag:unread and tag:inbox" :label "[A] " :face prot-modeline-indicator-green)
|
||||
(:terms "tag:unread and tag:inbox and not tag:package and not tag:coach" :label "[U] " :face prot-modeline-indicator-cyan)
|
||||
(:terms "tag:unread and tag:package" :label "[P] " :face prot-modeline-indicator-magenta)
|
||||
(:terms "tag:unread and tag:coach" :label "[C] " :face prot-modeline-indicator-red))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install notmuch-indicator)
|
||||
(with-eval-after-load 'notmuch
|
||||
(setq notmuch-indicator-args
|
||||
'(( :terms "tag:unread and tag:inbox"
|
||||
:label "[U] "
|
||||
;; :label "💬 "
|
||||
:label-face prot-modeline-indicator-cyan
|
||||
:counter-face prot-modeline-indicator-cyan)
|
||||
( :terms "tag:unread and tag:package"
|
||||
:label "[P] "
|
||||
;; :label "🛠️ "
|
||||
:label-face prot-modeline-indicator-magenta
|
||||
:counter-face prot-modeline-indicator-magenta)
|
||||
( :terms "tag:unread and tag:coach"
|
||||
:label "[C] "
|
||||
;; :label "🏆 "
|
||||
:label-face prot-modeline-indicator-red
|
||||
:counter-face prot-modeline-indicator-red))
|
||||
|
||||
notmuch-indicator-refresh-count (* 60 3)
|
||||
notmuch-indicator-hide-empty-counters t
|
||||
notmuch-indicator-force-refresh-commands '(notmuch-refresh-this-buffer))
|
||||
notmuch-indicator-refresh-count (* 60 3)
|
||||
notmuch-indicator-hide-empty-counters t
|
||||
notmuch-indicator-force-refresh-commands '(notmuch-refresh-this-buffer))
|
||||
|
||||
;; I control its placement myself. See prot-emacs-modeline.el where
|
||||
;; I set the `mode-line-format'.
|
||||
(setq notmuch-indicator-add-to-mode-line-misc-info nil)
|
||||
;; I control its placement myself. See prot-emacs-modeline.el where
|
||||
;; I set the `mode-line-format'.
|
||||
(setq notmuch-indicator-add-to-mode-line-misc-info nil)
|
||||
|
||||
(notmuch-indicator-mode 1))
|
||||
(notmuch-indicator-mode 1)))
|
||||
|
||||
(provide 'prot-emacs-notmuch)
|
||||
|
|
|
|||
|
|
@ -1,38 +1,52 @@
|
|||
;;; Calendar
|
||||
(use-package calendar
|
||||
:ensure nil
|
||||
:commands (calendar)
|
||||
:config
|
||||
(setq calendar-mark-diary-entries-flag nil)
|
||||
(setq calendar-mark-holidays-flag t)
|
||||
(setq calendar-mode-line-format nil)
|
||||
(setq calendar-time-display-form
|
||||
'( 24-hours ":" minutes
|
||||
(when time-zone (format "(%s)" time-zone))))
|
||||
(setq calendar-week-start-day 1) ; Monday
|
||||
(setq calendar-date-style 'iso)
|
||||
(setq calendar-time-zone-style 'numeric) ; Emacs 28.1
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'calendar
|
||||
(setq calendar-mark-diary-entries-flag nil)
|
||||
(setq calendar-mark-holidays-flag t)
|
||||
(setq calendar-mode-line-format nil)
|
||||
(setq calendar-time-display-form
|
||||
'( 24-hours ":" minutes
|
||||
(when time-zone (format "(%s)" time-zone))))
|
||||
(setq calendar-week-start-day 1) ; Monday
|
||||
(setq calendar-date-style 'iso)
|
||||
(setq calendar-time-zone-style 'numeric) ; Emacs 28.1
|
||||
|
||||
(require 'solar)
|
||||
(setq calendar-latitude 35.17 ; Not my actual coordinates
|
||||
calendar-longitude 33.36)
|
||||
(require 'solar)
|
||||
(setq calendar-latitude 35.17 ; Not my actual coordinates
|
||||
calendar-longitude 33.36)
|
||||
|
||||
(require 'cal-dst)
|
||||
(setq calendar-standard-time-zone-name "+0200")
|
||||
(setq calendar-daylight-time-zone-name "+0300"))
|
||||
(require 'cal-dst)
|
||||
(setq calendar-standard-time-zone-name "+0200")
|
||||
(setq calendar-daylight-time-zone-name "+0300")))
|
||||
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install-vc
|
||||
institution-calendar
|
||||
"https://github.com/protesilaos/institution-calendar.git")
|
||||
|
||||
;; If you want to permanently change what M-x calendar shows, enable
|
||||
;; `institution-calendar-mode'. Otherwise, use the relevant command:
|
||||
;;
|
||||
;; `institution-calendar-cambridge-university'
|
||||
;; `institution-calendar-oxford-university'
|
||||
;;
|
||||
;; And so on for newly registered institutions.
|
||||
|
||||
;; Remember to read the manual on how to register your own institution!
|
||||
(setopt institution-calendar-entity 'oxford-university)
|
||||
|
||||
(setopt institution-calendar-include-extra-week-numbers t)
|
||||
(setopt institution-calendar-include-intermonth-header nil))
|
||||
|
||||
;;; Appt (appointment reminders which also integrate with Org agenda)
|
||||
(use-package appt
|
||||
:ensure nil
|
||||
:commands (appt-activate)
|
||||
:config
|
||||
(setq appt-display-diary nil
|
||||
appt-display-format nil
|
||||
appt-display-mode-line t
|
||||
appt-display-interval 3
|
||||
appt-audible nil ; TODO 2023-01-25: t does nothing because I disable `ring-bell-function'?
|
||||
appt-warning-time-regexp "appt \\([0-9]+\\)" ; This is for the diary
|
||||
appt-message-warning-time 6)
|
||||
(prot-emacs-configure
|
||||
(setq appt-display-diary nil)
|
||||
(setq appt-display-format nil)
|
||||
(setq appt-display-mode-line t)
|
||||
(setq appt-display-interval 3)
|
||||
(setq appt-audible nil) ; TODO 2023-01-25: t does nothing because I disable `ring-bell-function'?
|
||||
(setq appt-warning-time-regexp "appt \\([0-9]+\\)") ; This is for the diary
|
||||
(setq appt-message-warning-time 6)
|
||||
|
||||
(with-eval-after-load 'org-agenda
|
||||
(appt-activate 1)
|
||||
|
|
@ -44,463 +58,464 @@
|
|||
(org-agenda-to-appt)))
|
||||
|
||||
;;; Org-mode (personal information manager)
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:init
|
||||
;; NOTE 2023-05-20: Must be evaluated before Org is loaded,
|
||||
;; otherwise we have to use the Custom UI. No thanks!
|
||||
(setq org-export-backends '(html texinfo md))
|
||||
|
||||
(prot-emacs-configure
|
||||
(setq org-directory (expand-file-name "~/Documents/org/"))
|
||||
(setq org-imenu-depth 7)
|
||||
|
||||
(add-to-list 'safe-local-variable-values '(org-hide-leading-stars . t))
|
||||
(add-to-list 'safe-local-variable-values '(org-hide-macro-markers . t))
|
||||
:bind
|
||||
( :map global-map
|
||||
("C-c l" . org-store-link)
|
||||
("C-c o" . org-open-at-point-global)
|
||||
:map org-mode-map
|
||||
;; I don't like that Org binds one zillion keys, so if I want one
|
||||
;; for something more important, I disable it from here.
|
||||
("C-'" . nil)
|
||||
("C-," . nil)
|
||||
("M-;" . nil)
|
||||
("<C-return>" . nil)
|
||||
("<C-S-return>" . nil)
|
||||
("C-M-S-<right>" . nil)
|
||||
("C-M-S-<left>" . nil)
|
||||
("C-c ;" . nil)
|
||||
("C-c M-l" . org-insert-last-stored-link)
|
||||
("C-c C-M-l" . org-toggle-link-display)
|
||||
("M-." . org-edit-special) ; alias for C-c ' (mnenomic is global M-. that goes to source)
|
||||
:map org-src-mode-map
|
||||
("M-," . org-edit-src-exit) ; see M-. above
|
||||
:map narrow-map
|
||||
("b" . org-narrow-to-block)
|
||||
("e" . org-narrow-to-element)
|
||||
("s" . org-narrow-to-subtree))
|
||||
:config
|
||||
;;;; general settings
|
||||
(setq org-ellipsis "⮧")
|
||||
(setq org-adapt-indentation nil) ; No, non, nein, όχι!
|
||||
(setq org-special-ctrl-a/e nil)
|
||||
(setq org-special-ctrl-k nil)
|
||||
(setq org-M-RET-may-split-line '((default . nil)))
|
||||
(setq org-hide-emphasis-markers nil)
|
||||
(setq org-hide-macro-markers nil)
|
||||
(setq org-hide-leading-stars nil)
|
||||
(setq org-cycle-separator-lines 0)
|
||||
(setq org-structure-template-alist
|
||||
'(("s" . "src")
|
||||
("e" . "src emacs-lisp")
|
||||
("E" . "src emacs-lisp :results value code :lexical t")
|
||||
("t" . "src emacs-lisp :tangle FILENAME")
|
||||
("T" . "src emacs-lisp :tangle FILENAME :mkdirp yes")
|
||||
("x" . "example")
|
||||
("X" . "export")
|
||||
("q" . "quote")))
|
||||
(setq org-catch-invisible-edits 'show)
|
||||
(setq org-return-follows-link nil)
|
||||
(setq org-loop-over-headlines-in-active-region 'start-level)
|
||||
(setq org-modules '(ol-info ol-eww))
|
||||
(setq org-use-sub-superscripts '{})
|
||||
(setq org-insert-heading-respect-content t)
|
||||
(setq org-read-date-prefer-future 'time)
|
||||
|
||||
;; See my `pulsar' package, defined elsewhere in this setup.
|
||||
(with-eval-after-load 'pulsar
|
||||
(dolist (hook '(org-agenda-after-show-hook org-follow-link-hook))
|
||||
(add-hook hook #'pulsar-recenter-center)
|
||||
(add-hook hook #'pulsar-reveal-entry))))
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c l" #'org-store-link
|
||||
"C-c o" #'org-open-at-point-global)
|
||||
|
||||
(with-eval-after-load 'org
|
||||
(prot-emacs-keybind org-mode-map
|
||||
;; I don't like that Org binds one zillion keys, so if I want one
|
||||
;; for something more important, I disable it from here.
|
||||
"C-a" nil
|
||||
"C-d" nil
|
||||
"C-S-d" nil
|
||||
"C-'" nil
|
||||
"C-," nil
|
||||
"M-;" nil
|
||||
"<C-return>" nil
|
||||
"<C-S-return>" nil
|
||||
"C-M-S-<right>" nil
|
||||
"C-M-S-<left>" nil
|
||||
"C-c ;" nil
|
||||
"C-c C-x C-c" nil ; disable `org-columns', which I never use
|
||||
"C-c M-l" #'org-insert-last-stored-link
|
||||
"C-c C-M-l" #'org-toggle-link-display
|
||||
"M-." #'org-edit-special) ; alias for C-c ' (mnenomic is global M-. that goes to source
|
||||
(define-key org-src-mode-map (kbd "M-,") #'org-edit-src-exit) ; see M-. above
|
||||
|
||||
(prot-emacs-keybind narrow-map
|
||||
"b" #'org-narrow-to-block
|
||||
"e" #'org-narrow-to-element
|
||||
"s" #'org-narrow-to-subtree)
|
||||
|
||||
(prot-emacs-keybind ctl-x-x-map
|
||||
"i" #'prot-org-id-headlines
|
||||
"r" #'prot-org-id-headlines-readable
|
||||
"h" #'prot-org-ox-html)
|
||||
|
||||
;; My custom extras, which I use for the agenda and a few other Org features.
|
||||
(require 'prot-org)
|
||||
|
||||
;;;; general settings
|
||||
(setq org-ellipsis " ▼")
|
||||
(setq org-special-ctrl-a/e nil)
|
||||
(setq org-special-ctrl-k nil)
|
||||
(setq org-M-RET-may-split-line '((default . nil)))
|
||||
(setq org-insert-heading-respect-content t)
|
||||
(setq org-hide-emphasis-markers nil)
|
||||
(setq org-hide-macro-markers nil)
|
||||
(setq org-hide-leading-stars nil)
|
||||
(setq org-cycle-separator-lines 0)
|
||||
(setq org-structure-template-alist
|
||||
'(("s" . "src")
|
||||
("e" . "src emacs-lisp")
|
||||
("E" . "src emacs-lisp :results value code :lexical t")
|
||||
("t" . "src emacs-lisp :tangle FILENAME")
|
||||
("T" . "src emacs-lisp :tangle FILENAME :mkdirp yes")
|
||||
("x" . "example")
|
||||
("X" . "export")
|
||||
("q" . "quote")))
|
||||
(setq org-fold-catch-invisible-edits 'show)
|
||||
(setq org-yank-folded-subtrees nil)
|
||||
(setq org-return-follows-link t)
|
||||
(setq org-loop-over-headlines-in-active-region 'start-level)
|
||||
(setq org-modules '(ol-info ol-eww))
|
||||
(setq org-use-sub-superscripts '{})
|
||||
(setq org-read-date-prefer-future 'time)
|
||||
(setq org-highlight-latex-and-related nil) ; other options affect elisp regexp in src blocks
|
||||
(setq org-fontify-quote-and-verse-blocks t)
|
||||
(setq org-fontify-whole-block-delimiter-line t)
|
||||
(setq org-track-ordered-property-with-tag t)
|
||||
(setq org-highest-priority ?A)
|
||||
(setq org-lowest-priority ?C)
|
||||
(setq org-default-priority ?A)
|
||||
(setq org-priority-faces nil)
|
||||
|
||||
;; See my `pulsar' package, defined elsewhere in this setup.
|
||||
(prot-emacs-hook
|
||||
(org-agenda-after-show-hook org-follow-link-hook)
|
||||
(pulsar-recenter-center pulsar-reveal-entry)
|
||||
nil
|
||||
pulsar)))
|
||||
|
||||
;;;; `org-indent-mode' and initial folding
|
||||
(prot-emacs-configure
|
||||
(add-hook 'org-mode-hook #'org-indent-mode)
|
||||
(setq org-indent-mode-turns-on-hiding-stars nil)
|
||||
(setq org-adapt-indentation nil) ; No, non, nein, όχι to literal indentation!
|
||||
(setq org-indent-indentation-per-level 4)
|
||||
(setq org-startup-folded 'content))
|
||||
|
||||
;;;; refile, todo
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:config
|
||||
(with-eval-after-load 'org
|
||||
(setq org-refile-targets
|
||||
'((org-agenda-files . (:maxlevel . 2))
|
||||
(nil . (:maxlevel . 2))))
|
||||
(setq org-refile-use-outline-path t)
|
||||
(setq org-refile-use-outline-path nil)
|
||||
(setq org-refile-allow-creating-parent-nodes 'confirm)
|
||||
(setq org-refile-use-cache t)
|
||||
(setq org-reverse-note-order nil)
|
||||
;; ;; NOTE 2023-04-07: Leaving this here for demo purposes.
|
||||
;; (setq org-todo-keywords
|
||||
;; '((sequence "TODO(t)" "MAYBE(m)" "WAIT(w@/!)" "|" "CANCEL(c@)" "DONE(d!)")
|
||||
;; (sequence "COACH(k)" "|" "COACHED(K!)")))
|
||||
(setq org-todo-keywords
|
||||
'((sequence "TODO(t)" "|" "CANCEL(c@)" "DONE(d!)")
|
||||
(sequence "COACH(k)" "|" "COACHED(K!)")))
|
||||
|
||||
(defface prot/org-bold-done
|
||||
'((t :inherit (bold org-done)))
|
||||
"Face for bold DONE-type Org keywords.")
|
||||
(setq org-todo-keywords
|
||||
'((sequence "TODO(t)" "MAYBE(m)" "|" "CANCELLED(c@)" "DONE(d!)")))
|
||||
|
||||
(defface prot/org-todo-alternative
|
||||
'((t :inherit (italic org-todo)))
|
||||
"Face for alternative TODO-type Org keywords.")
|
||||
|
||||
(defface prot/org-done-alternative
|
||||
'((t :inherit (italic org-done)))
|
||||
"Face for alternative DONE-type Org keywords.")
|
||||
|
||||
(setq org-todo-keyword-faces
|
||||
'(("CANCEL" . prot/org-bold-done)))
|
||||
'(("MAYBE" . prot/org-todo-alternative)
|
||||
("CANCELLED" . prot/org-done-alternative)))
|
||||
|
||||
(defface prot/org-tag-coaching
|
||||
'((default :inherit unspecified :weight regular :slant normal)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#004476")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#c0d0ef")
|
||||
(t :foreground "cyan"))
|
||||
"Face for coaching Org tag.")
|
||||
|
||||
(defface prot/org-tag-protasks
|
||||
'((default :inherit unspecified :weight regular :slant normal)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#603f00")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#deba66")
|
||||
(t :foreground "yellow"))
|
||||
"Face for protasks Org tag.")
|
||||
|
||||
(when (eq prot-emacs-load-theme-family 'modus)
|
||||
(setq org-tag-faces
|
||||
'(("coaching" . prot/org-tag-coaching)
|
||||
("protasks" . prot/org-tag-protasks))))
|
||||
|
||||
(setq org-use-fast-todo-selection 'expert)
|
||||
(setq org-priority-faces nil)
|
||||
|
||||
(setq org-fontify-done-headline nil)
|
||||
(setq org-fontify-todo-headline nil)
|
||||
(setq org-fontify-quote-and-verse-blocks t)
|
||||
(setq org-fontify-whole-heading-line nil)
|
||||
(setq org-fontify-whole-block-delimiter-line t)
|
||||
(setq org-highlight-latex-and-related nil) ; other options affect elisp regexp in src blocks
|
||||
(setq org-enforce-todo-dependencies t)
|
||||
(setq org-enforce-todo-checkbox-dependencies t)
|
||||
(setq org-track-ordered-property-with-tag t)
|
||||
(setq org-highest-priority ?A)
|
||||
(setq org-lowest-priority ?C)
|
||||
(setq org-default-priority ?A))
|
||||
(setq org-enforce-todo-checkbox-dependencies t))
|
||||
|
||||
;;;; tags
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:config
|
||||
(setq org-tag-alist ; I don't really use those, but whatever
|
||||
'(("meeting")
|
||||
("admin")
|
||||
("emacs")
|
||||
("modus")
|
||||
("politics")
|
||||
("economics")
|
||||
("philosophy")
|
||||
("book")
|
||||
("essay")
|
||||
("mail")
|
||||
("purchase")
|
||||
("hardware")
|
||||
("software")
|
||||
("website")))
|
||||
|
||||
(setq org-auto-align-tags nil)
|
||||
(setq org-tags-column 0))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'org
|
||||
(setq org-tag-alist nil)
|
||||
(setq org-auto-align-tags nil)
|
||||
(setq org-tags-column 0)))
|
||||
|
||||
;;;; log
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:config
|
||||
(setq org-log-done 'time)
|
||||
(setq org-log-into-drawer t)
|
||||
(setq org-log-note-clock-out nil)
|
||||
(setq org-log-redeadline 'time)
|
||||
(setq org-log-reschedule 'time))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'org
|
||||
(setq org-log-done 'time)
|
||||
(setq org-log-into-drawer t)
|
||||
(setq org-log-note-clock-out nil)
|
||||
(setq org-log-redeadline 'time)
|
||||
(setq org-log-reschedule 'time)))
|
||||
|
||||
;;;; links
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:config
|
||||
(setq org-link-keep-stored-after-insertion nil))
|
||||
;; TODO 2021-10-15 org-link-make-description-function
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'org
|
||||
(setq org-return-follows-link t)
|
||||
(setq org-link-context-for-files t)
|
||||
(setq org-link-keep-stored-after-insertion nil)
|
||||
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)))
|
||||
|
||||
;;;; code blocks
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:config
|
||||
(setq org-confirm-babel-evaluate nil)
|
||||
(setq org-src-window-setup 'current-window)
|
||||
(setq org-edit-src-persistent-message nil)
|
||||
(setq org-src-fontify-natively t)
|
||||
(setq org-src-preserve-indentation t)
|
||||
(setq org-src-tab-acts-natively t)
|
||||
(setq org-edit-src-content-indentation 0))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'org
|
||||
(setq org-confirm-babel-evaluate nil)
|
||||
(setq org-src-window-setup 'current-window)
|
||||
(setq org-edit-src-persistent-message nil)
|
||||
(setq org-src-fontify-natively t)
|
||||
(setq org-src-preserve-indentation t)
|
||||
(setq org-src-tab-acts-natively t)
|
||||
(setq org-edit-src-content-indentation 0)))
|
||||
|
||||
;;;; export
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:config
|
||||
(setq org-export-with-toc t)
|
||||
(setq org-export-headline-levels 8)
|
||||
(setq org-export-dispatch-use-expert-ui nil)
|
||||
(setq org-html-htmlize-output-type nil)
|
||||
(setq org-html-head-include-default-style nil)
|
||||
(setq org-html-head-include-scripts nil))
|
||||
(prot-emacs-configure
|
||||
;; NOTE 2023-05-20: Must be evaluated before Org is loaded,
|
||||
;; otherwise we have to use the Custom UI. No thanks!
|
||||
(setq org-export-backends '(html texinfo md))
|
||||
|
||||
;;;; IDs
|
||||
(use-package org
|
||||
:ensure nil
|
||||
:config
|
||||
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id))
|
||||
(with-eval-after-load 'org
|
||||
(setq org-export-with-toc t)
|
||||
(setq org-export-headline-levels 8)
|
||||
(setq org-export-dispatch-use-expert-ui nil)
|
||||
(setq org-html-htmlize-output-type nil)
|
||||
(setq org-html-head-include-default-style nil)
|
||||
(setq org-html-head-include-scripts nil)))
|
||||
|
||||
;;;; capture
|
||||
(use-package org-capture
|
||||
:ensure nil
|
||||
:bind ("C-c c" . org-capture)
|
||||
:config
|
||||
(require 'prot-org)
|
||||
(prot-emacs-configure
|
||||
(define-key global-map (kbd "C-c c") #'org-capture)
|
||||
|
||||
(setq org-capture-templates
|
||||
`(("b" "Basic task for future review" entry
|
||||
(file+headline "tasks.org" "Tasks to be reviewed")
|
||||
,(concat "* %^{Title}\n"
|
||||
":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":END:\n\n"
|
||||
"%i %l")
|
||||
:empty-lines-after 1)
|
||||
("c" "Clock in to a task" entry
|
||||
(file+headline "tasks.org" "Clocked tasks")
|
||||
,(concat "* TODO %^{Title}\n"
|
||||
":PROPERTIES:\n"
|
||||
":EFFORT: %^{Effort estimate in minutes|5|10|15|30|45|60|90|120}\n"
|
||||
":END:\n\n"
|
||||
"%a\n")
|
||||
:prepend t
|
||||
:clock-in t
|
||||
:clock-keep t
|
||||
:immediate-finish t
|
||||
:empty-lines-after 1)
|
||||
("m" "Memorandum of conversation" entry
|
||||
(file+headline "tasks.org" "Tasks to be reviewed")
|
||||
,(concat "* Memorandum of conversation with %^{Person}\n"
|
||||
":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":END:\n\n"
|
||||
"%i%?")
|
||||
:empty-lines-after 1)
|
||||
("t" "Task with a due date" entry
|
||||
(file+headline "tasks.org" "Tasks with a date")
|
||||
,(concat "* TODO %^{Title} %^g\n"
|
||||
"SCHEDULED: %^t\n"
|
||||
":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":END:\n\n"
|
||||
"%a\n%i%?")
|
||||
:empty-lines-after 1)
|
||||
("e" "Email note" entry
|
||||
(file+headline "tasks.org" "Tasks to be reviewed")
|
||||
,(concat "* TODO %:subject :mail:\n"
|
||||
":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":END:\n\n"
|
||||
"%a\n%i%?")
|
||||
:empty-lines-after 1)
|
||||
("p" "Private lesson or service" entry
|
||||
(file "coach.org")
|
||||
#'prot-org-capture-coach
|
||||
:prepend t
|
||||
:empty-lines 1)
|
||||
("P" "Private service clocked" entry
|
||||
(file+headline "coach.org" "Clocked services")
|
||||
#'prot-org-capture-coach-clock
|
||||
:prepend t
|
||||
:clock-in t
|
||||
:clock-keep t
|
||||
:immediate-finish t
|
||||
:empty-lines 1)))
|
||||
(with-eval-after-load 'org-capture
|
||||
|
||||
(setq org-capture-templates-contexts
|
||||
'(("e" ((in-mode . "notmuch-search-mode")
|
||||
(in-mode . "notmuch-show-mode")
|
||||
(in-mode . "notmuch-tree-mode"))))))
|
||||
(require 'prot-org)
|
||||
|
||||
(setq org-capture-templates
|
||||
(let* ((without-time (concat ":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":CUSTOM_ID: h:%(format-time-string \"%Y%m%dT%H%M%S\")\n"
|
||||
":END:\n\n"
|
||||
"%a\n%?"))
|
||||
(with-time (concat "DEADLINE: %^T\n"
|
||||
":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":CUSTOM_ID: h:%(format-time-string \"%Y%m%dT%H%M%S\")\n"
|
||||
":APPT_WARNTIME: 20\n"
|
||||
":END:\n\n"
|
||||
"%a%?")))
|
||||
`(("s" "Select file and heading to add to" entry
|
||||
(function prot-org-select-heading-in-file)
|
||||
,(concat "* TODO %^{Title}%?\n" without-time)
|
||||
:empty-lines-after 1)
|
||||
("t" "Task to do" entry
|
||||
(file+headline "tasks.org" "All tasks")
|
||||
,(concat "* TODO %^{Title} %^g\n" without-time)
|
||||
:empty-lines-after 1)
|
||||
("w" "Wishlist" entry
|
||||
(file+olp "tasks.org" "All tasks" "Wishlist")
|
||||
,(concat "* %^{Title} %^g\n" without-time)
|
||||
:empty-lines-after 1)
|
||||
("·" "") ; HACK to get a divider
|
||||
("a" "Appointment" entry
|
||||
(file+headline "tasks.org" "Appointments")
|
||||
,(concat "* TODO %^{Title}\n" with-time)
|
||||
:empty-lines-after 1)
|
||||
("c" "Coaching appointment" entry
|
||||
(file "coach.org")
|
||||
,(concat "* TODO %^{Person and description}\n" with-time)
|
||||
:prepend t
|
||||
:empty-lines 1)
|
||||
("p" "Prot Asks" entry
|
||||
(file+headline "tasks.org" "Prot Asks")
|
||||
,(concat "* TODO %^{Title}\n" with-time)
|
||||
:empty-lines-after 1)
|
||||
("v" "Video call" entry
|
||||
(file+headline "tasks.org" "Video calls")
|
||||
,(concat "* TODO %^{Title}\n" with-time)
|
||||
:empty-lines-after 1))))))
|
||||
|
||||
;;;; agenda
|
||||
(use-package org-agenda
|
||||
:ensure nil
|
||||
:bind
|
||||
(prot-emacs-configure
|
||||
;; I bind `org-agenda' to C-c A, so this one puts me straight into my
|
||||
;; custom block agenda.
|
||||
( :map global-map
|
||||
("C-c A" . org-agenda)
|
||||
("C-c a" . (lambda ()
|
||||
"Call Org agenda with `prot-org-custom-daily-agenda' configuration."
|
||||
(interactive)
|
||||
(org-agenda nil "A")))
|
||||
:map ctl-x-x-map
|
||||
("i" . prot-org-id-headlines)
|
||||
("h" . prot-org-ox-html))
|
||||
:config
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c A" #'org-agenda
|
||||
"C-c a" (lambda ()
|
||||
"Call Org agenda with `prot-org-custom-daily-agenda' configuration."
|
||||
(interactive)
|
||||
(org-agenda nil "A")))
|
||||
|
||||
(with-eval-after-load 'org-agenda
|
||||
(prot-emacs-keybind org-agenda-mode-map
|
||||
"n" #'org-agenda-next-item
|
||||
"p" #'org-agenda-previous-item)
|
||||
|
||||
;;;;; Custom agenda blocks
|
||||
|
||||
(require 'prot-org)
|
||||
(require 'prot-org)
|
||||
(setq org-agenda-format-date #'prot-org-agenda-format-date-aligned)
|
||||
|
||||
(setq org-agenda-format-date #'prot-org-agenda-format-date-aligned)
|
||||
|
||||
;; Check the variable `prot-org-custom-daily-agenda' in prot-org.el
|
||||
(setq org-agenda-custom-commands
|
||||
`(("A" "Daily agenda and top priority tasks"
|
||||
,prot-org-custom-daily-agenda
|
||||
((org-agenda-fontify-priorities nil)
|
||||
(org-agenda-prefix-format " %t %s")
|
||||
(org-agenda-dim-blocked-tasks nil)))
|
||||
("P" "Plain text daily agenda and top priorities"
|
||||
,prot-org-custom-daily-agenda
|
||||
((org-agenda-with-colors nil)
|
||||
(org-agenda-prefix-format "%t %s")
|
||||
(org-agenda-current-time-string ,(car (last org-agenda-time-grid)))
|
||||
(org-agenda-fontify-priorities nil)
|
||||
(org-agenda-remove-tags t))
|
||||
("agenda.txt"))))
|
||||
;; Check the variable `prot-org-custom-daily-agenda' in prot-org.el
|
||||
(setq org-agenda-custom-commands
|
||||
`(("A" "Daily agenda and top priority tasks"
|
||||
,prot-org-custom-daily-agenda
|
||||
((org-agenda-fontify-priorities nil)
|
||||
(org-agenda-dim-blocked-tasks nil)))
|
||||
("P" "Plain text daily agenda and top priorities"
|
||||
,prot-org-custom-daily-agenda
|
||||
((org-agenda-with-colors nil)
|
||||
(org-agenda-prefix-format "%t %s")
|
||||
(org-agenda-current-time-string ,(car (last org-agenda-time-grid)))
|
||||
(org-agenda-fontify-priorities nil)
|
||||
(org-agenda-remove-tags t))
|
||||
("agenda.txt"))
|
||||
("p" "Prot Asks"
|
||||
,prot-org-custom-prot-asks-agenda)))
|
||||
|
||||
;;;;; Basic agenda setup
|
||||
(setq org-default-notes-file (make-temp-file "emacs-org-notes-")) ; send it to oblivion
|
||||
(setq org-agenda-files `(,org-directory))
|
||||
(setq org-agenda-span 'week)
|
||||
(setq org-agenda-start-on-weekday 1) ; Monday
|
||||
(setq org-agenda-confirm-kill t)
|
||||
(setq org-agenda-show-all-dates t)
|
||||
(setq org-agenda-show-outline-path nil)
|
||||
(setq org-agenda-window-setup 'current-window)
|
||||
(setq org-agenda-skip-comment-trees t)
|
||||
(setq org-agenda-menu-show-matcher t)
|
||||
(setq org-agenda-menu-two-columns nil)
|
||||
(setq org-agenda-sticky nil)
|
||||
(setq org-agenda-custom-commands-contexts nil)
|
||||
(setq org-agenda-max-entries nil)
|
||||
(setq org-agenda-max-todos nil)
|
||||
(setq org-agenda-max-tags nil)
|
||||
(setq org-agenda-max-effort nil)
|
||||
(setq org-default-notes-file (make-temp-file "emacs-org-notes-")) ; send it to oblivion
|
||||
(setq org-agenda-files (list org-directory))
|
||||
(setq org-agenda-span 'week)
|
||||
(setq org-agenda-start-on-weekday 1) ; Monday
|
||||
(setq org-agenda-confirm-kill t)
|
||||
(setq org-agenda-show-all-dates t)
|
||||
(setq org-agenda-show-outline-path nil)
|
||||
(setq org-agenda-window-setup 'current-window)
|
||||
(setq org-agenda-skip-comment-trees t)
|
||||
(setq org-agenda-menu-show-matcher t)
|
||||
(setq org-agenda-menu-two-columns nil)
|
||||
(setq org-agenda-sticky nil)
|
||||
(setq org-agenda-custom-commands-contexts nil)
|
||||
(setq org-agenda-max-entries nil)
|
||||
(setq org-agenda-max-todos nil)
|
||||
(setq org-agenda-max-tags nil)
|
||||
(setq org-agenda-max-effort nil)
|
||||
|
||||
;;;;; General agenda view options
|
||||
;; NOTE 2021-12-07: Check further below my `org-agenda-custom-commands'
|
||||
(setq org-agenda-prefix-format
|
||||
'((agenda . " %i %-12:c%?-12t% s")
|
||||
(todo . " %i %-12:c")
|
||||
(tags . " %i %-12:c")
|
||||
(search . " %i %-12:c")))
|
||||
(setq org-agenda-sorting-strategy
|
||||
'(((agenda habit-down time-up priority-down category-keep)
|
||||
(todo priority-down category-keep)
|
||||
(tags priority-down category-keep)
|
||||
(search category-keep))))
|
||||
(setq org-agenda-breadcrumbs-separator "->")
|
||||
(setq org-agenda-todo-keyword-format "%-1s")
|
||||
(setq org-agenda-fontify-priorities 'cookies)
|
||||
(setq org-agenda-category-icon-alist nil)
|
||||
(setq org-agenda-remove-times-when-in-prefix nil)
|
||||
(setq org-agenda-remove-timeranges-from-blocks nil)
|
||||
(setq org-agenda-compact-blocks nil)
|
||||
(setq org-agenda-block-separator ?—)
|
||||
;; ;; NOTE 2021-12-07: Also see my `org-agenda-custom-commands'
|
||||
;; (setq org-agenda-prefix-format
|
||||
;; '((agenda . " %i %-12:c%?-12t% s")
|
||||
;; (todo . " %i %-12:c")
|
||||
;; (tags . " %i %-12:c")
|
||||
;; (search . " %i %-12:c")))
|
||||
(setq org-agenda-prefix-format "%c %t %s")
|
||||
(setq org-agenda-sorting-strategy
|
||||
'(((agenda habit-down time-up priority-down category-keep)
|
||||
(todo priority-down category-keep)
|
||||
(tags priority-down category-keep)
|
||||
(search category-keep))))
|
||||
(setq org-agenda-breadcrumbs-separator "->")
|
||||
(setq org-agenda-todo-keyword-format "%-1s")
|
||||
(setq org-agenda-fontify-priorities 'cookies)
|
||||
(setq org-agenda-category-icon-alist nil)
|
||||
(setq org-agenda-remove-times-when-in-prefix nil)
|
||||
(setq org-agenda-remove-timeranges-from-blocks nil)
|
||||
(setq org-agenda-compact-blocks nil)
|
||||
(setq org-agenda-block-separator ?—)
|
||||
|
||||
;;;;; Agenda marks
|
||||
(setq org-agenda-bulk-mark-char "#")
|
||||
(setq org-agenda-persistent-marks nil)
|
||||
(setq org-agenda-bulk-mark-char "#")
|
||||
(setq org-agenda-persistent-marks nil)
|
||||
|
||||
;;;;; Agenda diary entries
|
||||
(setq org-agenda-insert-diary-strategy 'date-tree)
|
||||
(setq org-agenda-insert-diary-extract-time nil)
|
||||
(setq org-agenda-include-diary nil)
|
||||
;; I do not want the diary, but there is no way to disable it
|
||||
;; altogether. This creates a diary file in the /tmp directory.
|
||||
(setq diary-file (make-temp-file "emacs-diary-"))
|
||||
(setq org-agenda-diary-file 'diary-file) ; TODO 2023-05-20: review Org diary substitute
|
||||
(setq org-agenda-insert-diary-strategy 'date-tree)
|
||||
(setq org-agenda-insert-diary-extract-time nil)
|
||||
(setq org-agenda-include-diary nil)
|
||||
;; I do not want the diary, but there is no way to disable it
|
||||
;; altogether. This creates a diary file in the /tmp directory.
|
||||
(setq diary-file (make-temp-file "emacs-diary-"))
|
||||
(setq org-agenda-diary-file 'diary-file) ; TODO 2023-05-20: review Org diary substitute
|
||||
|
||||
;;;;; Agenda follow mode
|
||||
(setq org-agenda-start-with-follow-mode nil)
|
||||
(setq org-agenda-follow-indirect t)
|
||||
(setq org-agenda-start-with-follow-mode nil)
|
||||
(setq org-agenda-follow-indirect t)
|
||||
|
||||
;;;;; Agenda multi-item tasks
|
||||
(setq org-agenda-dim-blocked-tasks t)
|
||||
(setq org-agenda-todo-list-sublevels t)
|
||||
(setq org-agenda-dim-blocked-tasks t)
|
||||
(setq org-agenda-todo-list-sublevels t)
|
||||
|
||||
;;;;; Agenda filters and restricted views
|
||||
(setq org-agenda-persistent-filter nil)
|
||||
(setq org-agenda-restriction-lock-highlight-subtree t)
|
||||
(setq org-agenda-persistent-filter nil)
|
||||
(setq org-agenda-restriction-lock-highlight-subtree t)
|
||||
|
||||
;;;;; Agenda items with deadline and scheduled timestamps
|
||||
(setq org-agenda-include-deadlines t)
|
||||
(setq org-deadline-warning-days 0)
|
||||
(setq org-agenda-skip-scheduled-if-done nil)
|
||||
(setq org-agenda-skip-scheduled-if-deadline-is-shown t)
|
||||
(setq org-agenda-skip-timestamp-if-deadline-is-shown t)
|
||||
(setq org-agenda-skip-deadline-if-done nil)
|
||||
(setq org-agenda-skip-deadline-prewarning-if-scheduled 1)
|
||||
(setq org-agenda-skip-scheduled-delay-if-deadline nil)
|
||||
(setq org-agenda-skip-additional-timestamps-same-entry nil)
|
||||
(setq org-agenda-skip-timestamp-if-done nil)
|
||||
(setq org-agenda-search-headline-for-time nil)
|
||||
(setq org-scheduled-past-days 365)
|
||||
(setq org-deadline-past-days 365)
|
||||
(setq org-agenda-move-date-from-past-immediately-to-today t)
|
||||
(setq org-agenda-show-future-repeats t)
|
||||
(setq org-agenda-prefer-last-repeat nil)
|
||||
(setq org-agenda-timerange-leaders
|
||||
'("" "(%d/%d): "))
|
||||
(setq org-agenda-scheduled-leaders
|
||||
'("Scheduled: " "Sched.%2dx: "))
|
||||
(setq org-agenda-inactive-leader "[")
|
||||
(setq org-agenda-deadline-leaders
|
||||
'("Deadline: " "In %3d d.: " "%2d d. ago: "))
|
||||
;; Time grid
|
||||
(setq org-agenda-time-leading-zero t)
|
||||
(setq org-agenda-timegrid-use-ampm nil)
|
||||
(setq org-agenda-use-time-grid t)
|
||||
(setq org-agenda-show-current-time-in-grid t)
|
||||
(setq org-agenda-current-time-string (concat "Now " (make-string 70 ?_)))
|
||||
(setq org-agenda-time-grid
|
||||
'((daily today require-timed)
|
||||
( 0500 0600 0700 0800 0900 1000
|
||||
1100 1200 1300 1400 1500 1600
|
||||
1700 1800 1900 2000 2100 2200)
|
||||
"" ""))
|
||||
(setq org-agenda-default-appointment-duration nil)
|
||||
(setq org-agenda-include-deadlines t)
|
||||
(setq org-deadline-warning-days 0)
|
||||
(setq org-agenda-skip-scheduled-if-done nil)
|
||||
(setq org-agenda-skip-scheduled-if-deadline-is-shown t)
|
||||
(setq org-agenda-skip-timestamp-if-deadline-is-shown t)
|
||||
(setq org-agenda-skip-deadline-if-done nil)
|
||||
(setq org-agenda-skip-deadline-prewarning-if-scheduled 1)
|
||||
(setq org-agenda-skip-scheduled-delay-if-deadline nil)
|
||||
(setq org-agenda-skip-additional-timestamps-same-entry nil)
|
||||
(setq org-agenda-skip-timestamp-if-done nil)
|
||||
(setq org-agenda-search-headline-for-time nil)
|
||||
(setq org-scheduled-past-days 365)
|
||||
(setq org-deadline-past-days 365)
|
||||
(setq org-agenda-move-date-from-past-immediately-to-today t)
|
||||
(setq org-agenda-show-future-repeats t)
|
||||
(setq org-agenda-prefer-last-repeat nil)
|
||||
(setq org-agenda-timerange-leaders
|
||||
'("" "(%d/%d): "))
|
||||
(setq org-agenda-scheduled-leaders
|
||||
'("Scheduled: " "Sched.%2dx: "))
|
||||
(setq org-agenda-inactive-leader "[")
|
||||
(setq org-agenda-deadline-leaders
|
||||
'("Deadline: " "In %3d d.: " "%2d d. ago: "))
|
||||
;; Time grid
|
||||
(setq org-agenda-time-leading-zero t)
|
||||
(setq org-agenda-timegrid-use-ampm nil)
|
||||
(setq org-agenda-use-time-grid t)
|
||||
(setq org-agenda-show-current-time-in-grid t)
|
||||
(setq org-agenda-current-time-string (concat "Now " (make-string 70 ?.)))
|
||||
(setq org-agenda-time-grid
|
||||
'((daily today require-timed)
|
||||
( 0600 0700 0800 0900 1000 1100
|
||||
1200 1300 1400 1500 1600 1700
|
||||
1800 1900 2000 2100 2200 2300)
|
||||
"" ""))
|
||||
(setq org-agenda-default-appointment-duration nil)
|
||||
|
||||
;;;;; Agenda global to-do list
|
||||
(setq org-agenda-todo-ignore-with-date t)
|
||||
(setq org-agenda-todo-ignore-timestamp t)
|
||||
(setq org-agenda-todo-ignore-scheduled t)
|
||||
(setq org-agenda-todo-ignore-deadlines t)
|
||||
(setq org-agenda-todo-ignore-time-comparison-use-seconds t)
|
||||
(setq org-agenda-tags-todo-honor-ignore-options nil)
|
||||
(setq org-agenda-todo-ignore-with-date t)
|
||||
(setq org-agenda-todo-ignore-timestamp t)
|
||||
(setq org-agenda-todo-ignore-scheduled t)
|
||||
(setq org-agenda-todo-ignore-deadlines t)
|
||||
(setq org-agenda-todo-ignore-time-comparison-use-seconds t)
|
||||
(setq org-agenda-tags-todo-honor-ignore-options nil)
|
||||
|
||||
;;;;; Agenda tagged items
|
||||
(setq org-agenda-show-inherited-tags t)
|
||||
(setq org-agenda-use-tag-inheritance
|
||||
'(todo search agenda))
|
||||
(setq org-agenda-hide-tags-regexp nil)
|
||||
(setq org-agenda-remove-tags nil)
|
||||
(setq org-agenda-tags-column -100)
|
||||
(setq org-agenda-show-inherited-tags t)
|
||||
(setq org-agenda-use-tag-inheritance
|
||||
'(todo search agenda))
|
||||
(setq org-agenda-hide-tags-regexp nil)
|
||||
(setq org-agenda-remove-tags nil)
|
||||
(setq org-agenda-tags-column 1)
|
||||
|
||||
;;;;; Agenda entry
|
||||
;; NOTE: I do not use this right now. Leaving everything to its
|
||||
;; default value.
|
||||
(setq org-agenda-start-with-entry-text-mode nil)
|
||||
(setq org-agenda-entry-text-maxlines 5)
|
||||
(setq org-agenda-entry-text-exclude-regexps nil)
|
||||
(setq org-agenda-entry-text-leaders " > ")
|
||||
;; NOTE: I do not use this right now. Leaving everything to its
|
||||
;; default value.
|
||||
(setq org-agenda-start-with-entry-text-mode nil)
|
||||
(setq org-agenda-entry-text-maxlines 5)
|
||||
(setq org-agenda-entry-text-exclude-regexps nil)
|
||||
(setq org-agenda-entry-text-leaders " > ")
|
||||
|
||||
;;;;; Agenda logging and clocking
|
||||
;; NOTE: I do not use these yet, though I plan to. Leaving everything
|
||||
;; to its default value for the time being.
|
||||
(setq org-agenda-log-mode-items '(closed clock))
|
||||
(setq org-agenda-clock-consistency-checks
|
||||
'((:max-duration "10:00" :min-duration 0 :max-gap "0:05" :gap-ok-around
|
||||
("4:00")
|
||||
:default-face ; This should definitely be reviewed
|
||||
((:background "DarkRed")
|
||||
(:foreground "white"))
|
||||
:overlap-face nil :gap-face nil :no-end-time-face nil
|
||||
:long-face nil :short-face nil)))
|
||||
(setq org-agenda-log-mode-add-notes t)
|
||||
(setq org-agenda-start-with-log-mode nil)
|
||||
(setq org-agenda-start-with-clockreport-mode nil)
|
||||
(setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel 2))
|
||||
(setq org-agenda-search-view-always-boolean nil)
|
||||
(setq org-agenda-search-view-force-full-words nil)
|
||||
(setq org-agenda-search-view-max-outline-level 0)
|
||||
(setq org-agenda-search-headline-for-time t)
|
||||
(setq org-agenda-use-time-grid t)
|
||||
(setq org-agenda-cmp-user-defined nil)
|
||||
(setq org-agenda-sort-notime-is-late t) ; Org 9.4
|
||||
(setq org-agenda-sort-noeffort-is-high t) ; Org 9.4
|
||||
;; NOTE: I do not use these yet, though I plan to. Leaving everything
|
||||
;; to its default value for the time being.
|
||||
(setq org-agenda-log-mode-items '(closed clock))
|
||||
(setq org-agenda-clock-consistency-checks
|
||||
'((:max-duration "10:00" :min-duration 0 :max-gap "0:05" :gap-ok-around
|
||||
("4:00")
|
||||
:default-face ; This should definitely be reviewed
|
||||
((:background "DarkRed")
|
||||
(:foreground "white"))
|
||||
:overlap-face nil :gap-face nil :no-end-time-face nil
|
||||
:long-face nil :short-face nil)))
|
||||
(setq org-agenda-log-mode-add-notes t)
|
||||
(setq org-agenda-start-with-log-mode nil)
|
||||
(setq org-agenda-start-with-clockreport-mode nil)
|
||||
(setq org-agenda-clockreport-parameter-plist '(:link t :maxlevel 2))
|
||||
(setq org-agenda-search-view-always-boolean nil)
|
||||
(setq org-agenda-search-view-force-full-words nil)
|
||||
(setq org-agenda-search-view-max-outline-level 0)
|
||||
(setq org-agenda-search-headline-for-time t)
|
||||
(setq org-agenda-use-time-grid t)
|
||||
(setq org-agenda-cmp-user-defined nil)
|
||||
(setq org-agenda-sort-notime-is-late t) ; Org 9.4
|
||||
(setq org-agenda-sort-noeffort-is-high t) ; Org 9.4
|
||||
|
||||
;; ;;;;; Agenda habits
|
||||
;; (require 'org-habit)
|
||||
;; (setq org-habit-graph-column 50)
|
||||
;; (setq org-habit-preceding-days 9)
|
||||
;; ;; Always show the habit graph, even if there are no habits for
|
||||
;; ;; today.
|
||||
;; (setq org-habit-show-all-today t)
|
||||
|
||||
;;;;; Agenda column view
|
||||
;; NOTE I do not use these, but may need them in the future.
|
||||
(setq org-agenda-view-columns-initially nil)
|
||||
(setq org-agenda-columns-show-summaries t)
|
||||
(setq org-agenda-columns-compute-summary-properties t)
|
||||
(setq org-agenda-columns-add-appointments-to-effort-sum nil)
|
||||
(setq org-agenda-auto-exclude-function nil)
|
||||
(setq org-agenda-bulk-custom-functions nil)
|
||||
|
||||
;; ;;;;; Agenda habits
|
||||
;; (require 'org-habit)
|
||||
;; (setq org-habit-graph-column 50)
|
||||
;; (setq org-habit-preceding-days 9)
|
||||
;; ;; Always show the habit graph, even if there are no habits for
|
||||
;; ;; today.
|
||||
;; (setq org-habit-show-all-today t)
|
||||
)
|
||||
;; NOTE I do not use these, but may need them in the future.
|
||||
(setq org-agenda-view-columns-initially nil)
|
||||
(setq org-agenda-columns-show-summaries t)
|
||||
(setq org-agenda-columns-compute-summary-properties t)
|
||||
(setq org-agenda-columns-add-appointments-to-effort-sum nil)
|
||||
(setq org-agenda-auto-exclude-function nil)
|
||||
(setq org-agenda-bulk-custom-functions nil)))
|
||||
|
||||
(provide 'prot-emacs-org)
|
||||
|
|
|
|||
|
|
@ -1,136 +1,122 @@
|
|||
;;; Isearch, occur, grep, and extras (prot-search.el)
|
||||
(use-package isearch
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(setq search-whitespace-regexp ".*?" ; one `setq' here to make it obvious they are a bundle
|
||||
isearch-lax-whitespace t
|
||||
isearch-regexp-lax-whitespace nil))
|
||||
(prot-emacs-configure
|
||||
(setq search-whitespace-regexp ".*?")
|
||||
(setq isearch-lax-whitespace t)
|
||||
(setq isearch-regexp-lax-whitespace nil))
|
||||
|
||||
(use-package isearch
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq search-highlight t)
|
||||
(setq isearch-lazy-highlight t)
|
||||
(setq lazy-highlight-initial-delay 0.5)
|
||||
(setq lazy-highlight-no-delay-length 4))
|
||||
|
||||
(use-package isearch
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq isearch-lazy-count t)
|
||||
(setq lazy-count-prefix-format "(%s/%s) ")
|
||||
(setq lazy-count-suffix-format nil))
|
||||
|
||||
(use-package isearch
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq isearch-wrap-pause t) ; `no-ding' makes keyboard macros never quit
|
||||
(setq isearch-repeat-on-direction-change t))
|
||||
|
||||
(use-package isearch
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(setq list-matching-lines-jump-to-current-line nil) ; do not jump to current line in `*occur*' buffers
|
||||
(add-hook 'occur-mode-hook #'prot-common-truncate-lines-silently) ; from `prot-common.el'
|
||||
(add-hook 'occur-mode-hook #'hl-line-mode))
|
||||
(prot-emacs-configure
|
||||
(setq query-replace-from-to-separator " > ")
|
||||
(defun prot/query-replace-preview (match replacement)
|
||||
"Do the work of `query-replace-show-preview'."
|
||||
(format "%s%s%s"
|
||||
(propertize match 'face 'query-replace-preview-match)
|
||||
(propertize query-replace-from-to-separator 'face 'secondary-selection)
|
||||
(propertize replacement 'face 'query-replace-preview)))
|
||||
(setq query-replace-show-preview #'prot/query-replace-preview)) ; Emacs 32
|
||||
|
||||
(use-package isearch
|
||||
:ensure nil
|
||||
:demand t
|
||||
:bind
|
||||
( :map minibuffer-local-isearch-map
|
||||
("M-/" . isearch-complete-edit)
|
||||
:map occur-mode-map
|
||||
("t" . toggle-truncate-lines)
|
||||
:map isearch-mode-map
|
||||
("C-g" . isearch-cancel) ; instead of `isearch-abort'
|
||||
("M-/" . isearch-complete)))
|
||||
(prot-emacs-configure
|
||||
(setq list-matching-lines-jump-to-current-line nil) ; do not jump to current line in `*occur*' buffers
|
||||
(prot-emacs-hook occur-mode-hook (prot-common-truncate-lines-silently hl-line-mode)))
|
||||
|
||||
(use-package prot-search
|
||||
:ensure nil
|
||||
:bind
|
||||
( :map global-map
|
||||
("M-s M-%" . prot-search-replace-markup) ; see `prot-search-markup-replacements'
|
||||
("M-s M-<" . prot-search-isearch-beginning-of-buffer)
|
||||
("M-s M->" . prot-search-isearch-end-of-buffer)
|
||||
("M-s g" . prot-search-grep)
|
||||
("M-s u" . prot-search-occur-urls)
|
||||
("M-s t" . prot-search-occur-todo-keywords)
|
||||
("M-s M-t" . prot-search-grep-todo-keywords) ; With C-u it runs `prot-search-git-grep-todo-keywords'
|
||||
("M-s M-T" . prot-search-git-grep-todo-keywords)
|
||||
("M-s s" . prot-search-outline)
|
||||
("M-s M-s" . prot-search-outline)
|
||||
("M-s M-o" . prot-search-occur-outline)
|
||||
("M-s M-u" . prot-search-occur-browse-url)
|
||||
:map isearch-mode-map
|
||||
("<up>" . prot-search-isearch-repeat-backward)
|
||||
("<down>" . prot-search-isearch-repeat-forward)
|
||||
("<backspace>" . prot-search-isearch-abort-dwim)
|
||||
("<C-return>" . prot-search-isearch-other-end))
|
||||
:config
|
||||
(setq prot-search-outline-regexp-alist
|
||||
'((emacs-lisp-mode . "^\\((\\|;;;+ \\)")
|
||||
(org-mode . "^\\(\\*+ +\\|#\\+[Tt][Ii][Tt][Ll][Ee]:\\)")
|
||||
(outline-mode . "^\\*+ +")
|
||||
(emacs-news-view-mode . "^\\*+ +")
|
||||
(conf-toml-mode . "^\\[")
|
||||
(markdown-mode . "^#+ +")))
|
||||
(setq prot-search-todo-keywords
|
||||
(concat "TODO\\|FIXME\\|NOTE\\|REVIEW\\|XXX\\|KLUDGE"
|
||||
"\\|HACK\\|WARN\\|WARNING\\|DEPRECATED\\|BUG"))
|
||||
(prot-emacs-configure
|
||||
(define-key global-map (kbd "C-.") #'isearch-forward-symbol-at-point) ; easier than M-s . // I also have `prot-simple-mark-sexp' on C-,
|
||||
(define-key minibuffer-local-isearch-map (kbd "M-/") #'isearch-complete-edit)
|
||||
(define-key occur-mode-map (kbd "t") #'toggle-truncate-lines)
|
||||
(prot-emacs-keybind isearch-mode-map
|
||||
"C-g" #'isearch-cancel ; instead of `isearch-abort'
|
||||
"M-/" #'isearch-complete))
|
||||
|
||||
(with-eval-after-load 'pulsar
|
||||
(add-hook 'prot-search-outline-hook #'pulsar-recenter-center)
|
||||
(add-hook 'prot-search-outline-hook #'pulsar-reveal-entry)))
|
||||
(prot-emacs-configure
|
||||
(require 'prot-search)
|
||||
(prot-emacs-keybind global-map
|
||||
"M-s M-%" #'prot-search-replace-markup ; see `prot-search-markup-replacements'
|
||||
"M-s g" #'prot-search-grep
|
||||
"M-s u" #'prot-search-occur-urls
|
||||
"M-s t" #'prot-search-occur-todo-keywords
|
||||
"M-s M-t" #'prot-search-grep-todo-keywords ; With C-u it runs `prot-search-git-grep-todo-keywords'
|
||||
"M-s M-T" #'prot-search-git-grep-todo-keywords
|
||||
"M-s s" #'prot-search-outline
|
||||
"M-s O" #'prot-search-collect
|
||||
"M-s M-o" #'prot-search-occur-outline
|
||||
"M-s M-u" #'prot-search-occur-browse-url)
|
||||
(prot-emacs-keybind isearch-mode-map
|
||||
"C-<home>" #'isearch-beginning-of-buffer
|
||||
"C-<end>" #'isearch-end-of-buffer
|
||||
"<up>" #'isearch-repeat-backward
|
||||
"<down>" #'isearch-repeat-forward
|
||||
"<backspace>" #'prot-search-isearch-abort-dwim
|
||||
"<C-return>" #'prot-search-isearch-other-end)
|
||||
(setq prot-search-outline-regexp-alist
|
||||
'((emacs-lisp-mode . "^\\((\\|;;;+ \\)")
|
||||
(org-mode . "^\\(\\*+ +\\|#\\+[Tt][Ii][Tt][Ll][Ee]:\\)")
|
||||
(outline-mode . "^\\*+ +")
|
||||
(emacs-news-view-mode . "^\\*+ +")
|
||||
(conf-toml-mode . "^\\[")
|
||||
(markdown-mode . "^#+ +")))
|
||||
(setq prot-search-todo-keywords
|
||||
(concat "TODO\\|FIXME\\|NOTE\\|REVIEW\\|XXX\\|KLUDGE"
|
||||
"\\|HACK\\|WARN\\|WARNING\\|DEPRECATED\\|BUG"))
|
||||
|
||||
(prot-emacs-hook
|
||||
prot-search-outline-hook
|
||||
(pulsar-recenter-center pulsar-reveal-entry)
|
||||
nil
|
||||
pulsar))
|
||||
|
||||
;;; grep and xref
|
||||
(use-package re-builder
|
||||
:ensure nil
|
||||
:commands (re-builder regexp-builder)
|
||||
:config
|
||||
(setq reb-re-syntax 'read))
|
||||
(prot-emacs-configure
|
||||
(setq reb-re-syntax 'read)
|
||||
|
||||
(setq xref-file-name-display 'project-relative)
|
||||
|
||||
(use-package xref
|
||||
:ensure nil
|
||||
:commands (xref-find-definitions xref-go-back)
|
||||
:config
|
||||
;; All those have been changed for Emacs 28
|
||||
(setq xref-show-definitions-function #'xref-show-definitions-completing-read) ; for M-.
|
||||
(setq xref-show-xrefs-function #'xref-show-definitions-buffer) ; for grep and the like
|
||||
(setq xref-file-name-display 'project-relative))
|
||||
|
||||
(use-package grep
|
||||
:ensure nil
|
||||
:commands (grep lgrep rgrep)
|
||||
:config
|
||||
;; The `outline-xref' is for Emacs 32
|
||||
(with-eval-after-load 'outline
|
||||
(define-advice outline-xref (:around (&rest args) prot)
|
||||
(let ((xref-show-xrefs-function #'xref-show-definitions-completing-read))
|
||||
(apply args))))
|
||||
|
||||
(setq grep-save-buffers nil)
|
||||
(setq grep-use-headings t) ; Emacs 30
|
||||
(setq grep-use-headings nil) ; Emacs 30
|
||||
|
||||
(let ((executable (or (executable-find "rg") "grep"))
|
||||
(rgp (string-match-p "rg" grep-program)))
|
||||
(setq grep-program executable)
|
||||
(setq grep-template
|
||||
(if rgp
|
||||
"/usr/bin/rg -nH --null -e <R> <F>"
|
||||
"/usr/bin/grep <X> <C> -nH --null -e <R> <F>"))
|
||||
(setq xref-search-program (if rgp 'ripgrep 'grep))))
|
||||
(if-let* ((ripgrep (or (executable-find "rg") (executable-find "ripgrep"))))
|
||||
(progn
|
||||
(setq grep-program ripgrep)
|
||||
(setq xref-search-program 'ripgrep)
|
||||
(setq grep-template (format "%s -nH --null -e <R> <F>" ripgrep)))
|
||||
(error "You need to install RIPGREP"))
|
||||
|
||||
(add-hook 'grep-mode #'prot-common-truncate-lines-silently))
|
||||
|
||||
;;; wgrep (writable grep)
|
||||
(use-package wgrep
|
||||
:ensure t
|
||||
:after grep
|
||||
:bind
|
||||
( :map grep-mode-map
|
||||
("e" . wgrep-change-to-wgrep-mode)
|
||||
("C-x C-q" . wgrep-change-to-wgrep-mode)
|
||||
("C-c C-c" . wgrep-finish-edit))
|
||||
:config
|
||||
(setq wgrep-auto-save-buffer t)
|
||||
(setq wgrep-change-readonly-file t))
|
||||
;; See the `grep-edit-mode' for the new built-in feature.
|
||||
(unless (>= emacs-major-version 31)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install wgrep)
|
||||
(with-eval-after-load 'grep
|
||||
(prot-emacs-keybind grep-mode-map
|
||||
"e" #'wgrep-change-to-wgrep-mode
|
||||
"C-x C-q" #'wgrep-change-to-wgrep-mode
|
||||
"C-c C-c" #'wgrep-finish-edit)
|
||||
(setq wgrep-auto-save-buffer t)
|
||||
(setq wgrep-change-readonly-file t))))
|
||||
|
||||
(provide 'prot-emacs-search)
|
||||
|
|
|
|||
|
|
@ -1,102 +1,59 @@
|
|||
;;; The Standard themes
|
||||
|
||||
;; The themes are customisable. Read the manual:
|
||||
;; <https://protesilaos.com/emacs/standard-themes>.
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install standard-themes)
|
||||
|
||||
(use-package standard-themes
|
||||
:ensure t
|
||||
:demand t
|
||||
:bind ("<f5>" . standard-themes-toggle)
|
||||
:config
|
||||
(setq standard-themes-bold-constructs t
|
||||
standard-themes-italic-constructs t
|
||||
standard-themes-mixed-fonts t
|
||||
standard-themes-variable-pitch-ui t
|
||||
standard-themes-mode-line-accented nil
|
||||
(standard-themes-take-over-modus-themes-mode 1)
|
||||
|
||||
;; Accepts a symbol value
|
||||
standard-themes-fringes 'subtle
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'modus-themes-rotate
|
||||
"C-<f5>" #'modus-themes-select
|
||||
"M-<f5>" #'modus-themes-load-random)
|
||||
|
||||
;; The following accept lists of properties
|
||||
standard-themes-links nil
|
||||
standard-themes-region nil
|
||||
standard-themes-prompts nil
|
||||
|
||||
;; more complex alist to set weight, height, and optional
|
||||
;; `variable-pitch' per heading level (t is for any level not
|
||||
;; specified)
|
||||
standard-themes-headings
|
||||
(setq modus-themes-variable-pitch-ui t
|
||||
modus-themes-mixed-fonts t
|
||||
modus-themes-to-rotate nil ; defaults to the return value of `modus-themes-get-themes'
|
||||
modus-themes-headings ; read the manual's entry of the doc string
|
||||
'((0 . (variable-pitch light 1.9))
|
||||
(1 . (variable-pitch light 1.8))
|
||||
(2 . (variable-pitch light 1.7))
|
||||
(3 . (variable-pitch semilight 1.6))
|
||||
(4 . (variable-pitch semilight 1.5))
|
||||
(5 . (variable-pitch 1.4))
|
||||
(2 . (variable-pitch regular 1.7))
|
||||
(3 . (variable-pitch regular 1.6))
|
||||
(4 . (variable-pitch regular 1.5))
|
||||
(5 . (variable-pitch 1.4)) ; absence of weight means `bold'
|
||||
(6 . (variable-pitch 1.3))
|
||||
(7 . (variable-pitch 1.2))
|
||||
(agenda-date . (1.3))
|
||||
(agenda-structure . (variable-pitch light 1.8))
|
||||
(agenda-date . (semilight 1.5))
|
||||
(agenda-structure . (variable-pitch light 1.9))
|
||||
(t . (variable-pitch 1.1))))
|
||||
|
||||
;; Load a theme that is consistent with my session's theme. Those
|
||||
;; functions are defined in my init.el.
|
||||
(if (prot-emacs-theme-environment-dark-p)
|
||||
(standard-themes-load-dark)
|
||||
(standard-themes-load-light)))
|
||||
;; The `standard-themes' provide lots of themes. I want to pick one at
|
||||
;; random when I start Emacs: the `modus-themes-load-random' does just
|
||||
;; that (it can be called interactively as well). I just check with
|
||||
;; my desktop environment to determine if the choice should be about
|
||||
;; a light or a dark theme. Those functions are in my init.el.
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
(modus-themes-load-random 'dark)
|
||||
(modus-themes-load-random 'light)))
|
||||
|
||||
;; NOTE: For testing purposes
|
||||
;; For testing purposes...
|
||||
(prot-emacs-comment
|
||||
(:eval nil)
|
||||
(progn
|
||||
(mapc #'disable-theme custom-enabled-themes)
|
||||
|
||||
(add-to-list 'load-path "/home/prot/Git/Projects/standard-themes/")
|
||||
(add-to-list 'load-path "~/Git/Projects/modus-themes/")
|
||||
(add-to-list 'load-path "~/Git/Projects/standard-themes/")
|
||||
|
||||
(require 'standard-themes)
|
||||
(load-theme 'standard-dark t t)
|
||||
(load-theme 'standard-light t t)
|
||||
(require 'modus-themes)
|
||||
(require 'standard-themes)
|
||||
|
||||
(setq standard-themes-bold-constructs t
|
||||
standard-themes-italic-constructs t
|
||||
standard-themes-disable-other-themes t
|
||||
standard-themes-mixed-fonts t
|
||||
standard-themes-variable-pitch-ui t
|
||||
standard-themes-mode-line-accented nil
|
||||
(standard-themes-take-over-modus-themes-mode 1)
|
||||
|
||||
;; Accepts a symbol value
|
||||
standard-themes-fringes 'subtle
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'modus-themes-rotate
|
||||
"C-<f5>" #'modus-themes-select
|
||||
"M-<f5>" #'modus-themes-load-random)
|
||||
|
||||
;; The following accept lists of properties
|
||||
standard-themes-links nil
|
||||
standard-themes-region nil
|
||||
standard-themes-prompts nil
|
||||
|
||||
;; more complex alist to set weight, height, and optional
|
||||
;; `variable-pitch' per heading level (t is for any level not
|
||||
;; specified)
|
||||
standard-themes-headings
|
||||
'((0 . (variable-pitch light 1.9))
|
||||
(1 . (variable-pitch light 1.8))
|
||||
(2 . (variable-pitch light 1.7))
|
||||
(3 . (variable-pitch semilight 1.6))
|
||||
(4 . (variable-pitch semilight 1.5))
|
||||
(5 . (variable-pitch 1.4))
|
||||
(6 . (variable-pitch 1.3))
|
||||
(7 . (variable-pitch 1.2))
|
||||
(agenda-date . (1.3))
|
||||
(agenda-structure . (variable-pitch light 1.8))
|
||||
(t . (variable-pitch 1.1))))
|
||||
|
||||
(mapcar (lambda (theme)
|
||||
(add-to-list
|
||||
'custom-theme-load-path
|
||||
(concat "/home/prot/Git/Projects/standard-themes/" (symbol-name theme) "-theme.el")))
|
||||
(standard-themes--list-enabled-themes))
|
||||
|
||||
(if (prot-emacs-theme-environment-dark-p)
|
||||
(load-theme 'standard-dark :no-confirm)
|
||||
(load-theme 'standard-light :no-confirm))
|
||||
|
||||
(define-key global-map (kbd "<f5>") #'standard-themes-toggle)))
|
||||
(if (prot-emacs-gnome-prefers-dark-p)
|
||||
(modus-themes-load-random 'dark)
|
||||
(modus-themes-load-random 'light)))
|
||||
|
||||
(provide 'prot-emacs-standard-themes)
|
||||
|
|
|
|||
|
|
@ -2,305 +2,333 @@
|
|||
|
||||
;;;; Load the desired theme module
|
||||
;; These all reference my packages: `modus-themes', `ef-themes',
|
||||
;; `standard-themes'.
|
||||
;; `doric-themes', `standard-themes'.
|
||||
(when prot-emacs-load-theme-family
|
||||
(require
|
||||
(pcase prot-emacs-load-theme-family
|
||||
('ef 'prot-emacs-ef-themes)
|
||||
('modus 'prot-emacs-modus-themes)
|
||||
('ef 'prot-emacs-ef-themes)
|
||||
('doric 'prot-emacs-doric-themes)
|
||||
('standard 'prot-emacs-standard-themes))))
|
||||
|
||||
;;;; Pulsar
|
||||
;; Read the pulsar manual: <https://protesilaos.com/emacs/pulsar>.
|
||||
(use-package pulsar
|
||||
:ensure t
|
||||
:config
|
||||
(setopt pulsar-pulse t
|
||||
pulsar-delay 0.055
|
||||
pulsar-iterations 10
|
||||
pulsar-face 'pulsar-magenta
|
||||
pulsar-highlight-face 'pulsar-cyan)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install pulsar)
|
||||
|
||||
(pulsar-global-mode 1)
|
||||
:hook
|
||||
;; There are convenience functions/commands which pulse the line using
|
||||
;; a specific colour: `pulsar-pulse-line-red' is one of them.
|
||||
((next-error . (pulsar-pulse-line-red pulsar-recenter-top pulsar-reveal-entry))
|
||||
(minibuffer-setup . pulsar-pulse-line-red))
|
||||
:bind
|
||||
;; pulsar does not define any key bindings. This is just my personal
|
||||
;; preference. Remember to read the manual on the matter. Evaluate:
|
||||
;;
|
||||
;; (info "(elisp) Key Binding Conventions")
|
||||
(("C-x l" . pulsar-pulse-line) ; override `count-lines-page'
|
||||
("C-x L" . pulsar-highlight-dwim))) ; or use `pulsar-highlight-line'
|
||||
|
||||
(prot-emacs-hook
|
||||
(next-error-hook minibuffer-setup-hook)
|
||||
(pulsar-pulse-line-red pulsar-recenter-top pulsar-reveal-entry))
|
||||
|
||||
(setq pulsar-delay 0.055)
|
||||
(setq pulsar-iterations 5)
|
||||
(setq pulsar-face 'pulsar-green)
|
||||
(setq pulsar-region-face 'pulsar-yellow)
|
||||
(setq pulsar-highlight-face 'pulsar-magenta)
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x l" #'pulsar-pulse-line ; override `count-lines-page'
|
||||
"C-x L" #'pulsar-highlight-permanently-dwim)) ; or use `pulsar-highlight-temporarily-dwim'
|
||||
|
||||
;;;; Lin
|
||||
;; Read the lin manual: <https://protesilaos.com/emacs/lin>.
|
||||
(use-package lin
|
||||
:ensure t
|
||||
:hook (after-init . lin-global-mode) ; applies to all `lin-mode-hooks'
|
||||
:config
|
||||
;; You can use this to live update the face:
|
||||
;;
|
||||
;; (customize-set-variable 'lin-face 'lin-green)
|
||||
;;
|
||||
;; Or `setopt' on Emacs 29: (setopt lin-face 'lin-yellow)
|
||||
;;
|
||||
;; I still prefer `setq' for consistency.
|
||||
(setq lin-face 'lin-magenta))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install lin)
|
||||
(setq lin-face 'lin-slate)
|
||||
(lin-global-mode 1)
|
||||
(when (string= (getenv "DESKTOP_SESSION") "gnome")
|
||||
(lin-gnome-accent-color-mode 1)))
|
||||
|
||||
;;;; Increase padding of windows/frames
|
||||
;; Yet another one of my packages:
|
||||
;; <https://protesilaos.com/codelog/2023-06-03-emacs-spacious-padding/>.
|
||||
(use-package spacious-padding
|
||||
:ensure t
|
||||
:if (display-graphic-p)
|
||||
:hook (after-init . spacious-padding-mode)
|
||||
:bind ("<f8>" . spacious-padding-mode)
|
||||
:init
|
||||
;; These are the defaults, but I keep it here for visiibility.
|
||||
(setq spacious-padding-widths
|
||||
'( :internal-border-width 15
|
||||
:header-line-width 4
|
||||
:mode-line-width 6
|
||||
:tab-width 4
|
||||
:right-divider-width 1
|
||||
:scroll-bar-width 8
|
||||
:left-fringe-width 20
|
||||
:right-fringe-width 20))
|
||||
(when prot-display-graphic-p
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install spacious-padding)
|
||||
|
||||
;; ;; Read the doc string of `spacious-padding-subtle-mode-line' as
|
||||
;; ;; it is very flexible.
|
||||
;;
|
||||
;; (setq spacious-padding-subtle-mode-line
|
||||
;; `( :mode-line-active ,(if (or (eq prot-emacs-load-theme-family 'modus)
|
||||
;; (eq prot-emacs-load-theme-family 'standard))
|
||||
;; 'default
|
||||
;; 'help-key-binding)
|
||||
;; :mode-line-inactive vertical-border))
|
||||
(spacious-padding-mode 1)
|
||||
|
||||
(setq spacious-padding-subtle-mode-line nil))
|
||||
(define-key global-map (kbd "<f8>") #'spacious-padding-mode)
|
||||
|
||||
;;;; Rainbow mode for colour previewing (rainbow-mode.el)
|
||||
(use-package rainbow-mode
|
||||
:ensure t
|
||||
:init
|
||||
(setq rainbow-ansi-colors nil)
|
||||
(setq rainbow-x-colors nil)
|
||||
(setq spacious-padding-widths
|
||||
`( :internal-border-width 15
|
||||
:header-line-width 4
|
||||
:mode-line-width 6
|
||||
:tab-width 4
|
||||
:right-divider-width 15
|
||||
:scroll-bar-width ,(if prot-pgtk-p 12 6)
|
||||
:left-fringe-width 8
|
||||
:right-fringe-width 20))
|
||||
|
||||
(defun prot/rainbow-mode-in-themes ()
|
||||
(when-let ((file (buffer-file-name))
|
||||
((derived-mode-p 'emacs-lisp-mode))
|
||||
((string-match-p "-theme" file)))
|
||||
(rainbow-mode 1)))
|
||||
:bind ( :map ctl-x-x-map
|
||||
("c" . rainbow-mode)) ; C-x x c
|
||||
:hook (emacs-lisp-mode . prot/rainbow-mode-in-themes))
|
||||
;; (setq spacious-padding-subtle-mode-line nil)
|
||||
|
||||
;; Read the doc string of `spacious-padding-subtle-mode-line' as it
|
||||
;; is very flexible. Here we make the mode lines be a single
|
||||
;; overline, while header lines have an underline.
|
||||
(setq spacious-padding-subtle-frame-lines
|
||||
'( :mode-line-active spacious-padding-line-active
|
||||
:mode-line-inactive spacious-padding-line-inactive
|
||||
:header-line-active spacious-padding-line-active
|
||||
:header-line-inactive spacious-padding-line-inactive))
|
||||
|
||||
(when (< emacs-major-version 29)
|
||||
(setq x-underline-at-descent-line (when spacious-padding-subtle-frame-lines t)))))
|
||||
|
||||
;;; Cursor appearance (cursory)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/cursory>.
|
||||
(use-package cursory
|
||||
:ensure t
|
||||
:demand t
|
||||
:if (display-graphic-p)
|
||||
:config
|
||||
(setq cursory-presets
|
||||
'((box
|
||||
:blink-cursor-interval 1.2)
|
||||
(box-no-blink
|
||||
:blink-cursor-mode -1)
|
||||
(bar
|
||||
:cursor-type (bar . 2)
|
||||
:blink-cursor-interval 0.5)
|
||||
(bar-no-other-window
|
||||
:inherit bar
|
||||
:cursor-in-non-selected-windows nil)
|
||||
(underscore
|
||||
:cursor-type (hbar . 3)
|
||||
:blink-cursor-blinks 50)
|
||||
(underscore-thin-other-window
|
||||
:inherit underscore
|
||||
:cursor-in-non-selected-windows (hbar . 1))
|
||||
(underscore-thick
|
||||
:cursor-type (hbar . 8)
|
||||
:blink-cursor-interval 0.3
|
||||
:blink-cursor-blinks 50
|
||||
:cursor-in-non-selected-windows (hbar . 3))
|
||||
(underscore-thick-no-blink
|
||||
:blink-cursor-mode -1
|
||||
:cursor-type (hbar . 8)
|
||||
:cursor-in-non-selected-windows (hbar . 3))
|
||||
(t ; the default values
|
||||
:cursor-type box
|
||||
:cursor-in-non-selected-windows hollow
|
||||
:blink-cursor-mode 1
|
||||
:blink-cursor-blinks 10
|
||||
:blink-cursor-interval 0.2
|
||||
:blink-cursor-delay 0.2)))
|
||||
(when prot-display-graphic-p
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install cursory)
|
||||
|
||||
;; I am using the default values of `cursory-latest-state-file'.
|
||||
(require 'cursory)
|
||||
|
||||
;; Set last preset or fall back to desired style from `cursory-presets'.
|
||||
(cursory-set-preset (or (cursory-restore-latest-preset) 'box))
|
||||
:hook
|
||||
;; The other side of `cursory-restore-latest-preset'.
|
||||
(kill-emacs . cursory-store-latest-preset)
|
||||
:bind
|
||||
;; We have to use the "point" mnemonic, because C-c c is often the
|
||||
;; suggested binding for `org-capture' and is the one I use as well.
|
||||
("C-c p" . cursory-set-preset))
|
||||
(setq cursory-presets
|
||||
'((box
|
||||
:blink-cursor-interval 1.2)
|
||||
(box-no-blink
|
||||
:inherit box
|
||||
:blink-cursor-mode -1)
|
||||
(bar
|
||||
:cursor-type (bar . 2)
|
||||
:cursor-color error ; will typically be red
|
||||
:blink-cursor-interval 0.8)
|
||||
(bar-no-other-window
|
||||
:inherit bar
|
||||
:cursor-in-non-selected-windows nil)
|
||||
(bar-no-blink
|
||||
:inherit bar
|
||||
:blink-cursor-mode -1)
|
||||
(underscore
|
||||
:cursor-color warning ; will typically be yellow
|
||||
:cursor-type (hbar . 3)
|
||||
:blink-cursor-blinks 50)
|
||||
(underscore-no-other-window
|
||||
:inherit underscore
|
||||
:cursor-in-non-selected-windows nil)
|
||||
(underscore-thick
|
||||
:inherit underscore
|
||||
:cursor-type (hbar . 8)
|
||||
:cursor-in-non-selected-windows (hbar . 3))
|
||||
(t ; the default values
|
||||
:cursor-color unspecified ; use the theme's original
|
||||
:cursor-type box
|
||||
:cursor-in-non-selected-windows hollow
|
||||
:blink-cursor-mode 1
|
||||
:blink-cursor-blinks 10
|
||||
:blink-cursor-interval 0.2
|
||||
:blink-cursor-delay 0.2)))
|
||||
|
||||
(cursory-set-last-or-fallback)
|
||||
|
||||
;; Persist configurations between Emacs sessions. Also apply the
|
||||
;; :cursor-color again when swithcing to another theme.
|
||||
(cursory-mode 1)
|
||||
|
||||
;; We have to use the "point" mnemonic, because C-c c is often the
|
||||
;; suggested binding for `org-capture' and is the one I use as well.
|
||||
(define-key global-map (kbd "C-c p") #'cursory-set-preset)
|
||||
|
||||
(with-eval-after-load 'prot-streaming
|
||||
(defvar prot/cursory-streaming--last-preset nil
|
||||
"The last Cursory preset before enabling `prot/cursory-streaming-mode'.")
|
||||
|
||||
(defun prot/cursory-streaming-mode ()
|
||||
"Set the Cursory preset to `box-no-blink' when streaming."
|
||||
(cond
|
||||
(prot-streaming-mode
|
||||
(setq prot/cursory-streaming-mode cursory-last-selected-preset)
|
||||
(cursory-set-preset 'box-no-blink))
|
||||
(prot/cursory-streaming-mode
|
||||
(cursory-set-preset prot/cursory-streaming-mode)
|
||||
(setq prot/cursory-streaming-mode nil))
|
||||
(t
|
||||
(cursory-set-last-or-fallback))))
|
||||
|
||||
(add-hook 'prot-streaming-mode-hook #'prot/cursory-streaming-mode))))
|
||||
|
||||
;;; GNOME accent theme switcher
|
||||
(when (and (null prot-emacs-load-theme-family)
|
||||
(string= (getenv "DESKTOP_SESSION") "gnome"))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install-vc
|
||||
gnome-accent-theme-switcher
|
||||
"https://github.com/protesilaos/gnome-accent-theme-switcher.git")
|
||||
(prot-emacs-keybind global-map
|
||||
"<f5>" #'gnome-accent-theme-switcher-toggle-mode
|
||||
"C-<f5>" #'gnome-accent-theme-switcher-change-accent
|
||||
"M-<f5>" #'gnome-accent-theme-switcher-load-random-from-current-set)
|
||||
(gnome-accent-theme-switcher-mode 1)))
|
||||
|
||||
;;; Modus themes exporter
|
||||
(prot-emacs-configure
|
||||
;; The use the command `modus-themes-exporter-export'.
|
||||
(prot-emacs-install-vc
|
||||
modus-themes-exporter
|
||||
"https://github.com/protesilaos/modus-themes-exporter.git"))
|
||||
|
||||
;;;; Theme buffet
|
||||
(use-package theme-buffet
|
||||
:ensure t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install theme-buffet)
|
||||
|
||||
(setq theme-buffet-menu 'end-user)
|
||||
(setq theme-buffet--end-user
|
||||
(setq theme-buffet-end-user
|
||||
'( :night (modus-vivendi ef-dark ef-winter ef-autumn ef-night ef-duo-dark ef-symbiosis)
|
||||
:morning (modus-operandi ef-light ef-cyprus ef-spring ef-frost ef-duo-light)
|
||||
:afternoon (modus-operandi-tinted ef-arbutus ef-day ef-kassio ef-summer ef-elea-light ef-maris-light ef-melissa-light ef-trio-light)
|
||||
:evening (modus-vivendi-tinted ef-rosa ef-elea-dark ef-maris-dark ef-melissa-dark ef-trio-dark)))
|
||||
:afternoon (modus-operandi-tinted ef-arbutus ef-day ef-kassio ef-summer ef-elea-light ef-maris-light ef-melissa-light ef-trio-light ef-reverie)
|
||||
:evening (modus-vivendi-tinted ef-rosa ef-elea-dark ef-maris-dark ef-melissa-dark ef-trio-dark ef-dream)))
|
||||
|
||||
(theme-buffet-timer-hours 1))
|
||||
|
||||
;;;; Fontaine (font configurations)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/fontaine>
|
||||
(use-package fontaine
|
||||
:ensure t
|
||||
:demand t
|
||||
:if (display-graphic-p)
|
||||
:bind ("C-c f" . fontaine-set-preset)
|
||||
:config
|
||||
;; This is defined in Emacs C code: it belongs to font settings.
|
||||
(setq x-underline-at-descent-line nil)
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install fontaine)
|
||||
|
||||
;; And this is for Emacs28.
|
||||
(setq-default text-scale-remap-header-line t)
|
||||
(fontaine-mode 1)
|
||||
|
||||
;; This is the default value. Just including it here for
|
||||
;; completeness.
|
||||
(setq fontaine-latest-state-file (locate-user-emacs-file "fontaine-latest-state.eld"))
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c f" #'fontaine-set-preset
|
||||
"C-c F" #'fontaine-toggle-preset)
|
||||
|
||||
(setq-default text-scale-remap-header-line t) ; Emacs 28
|
||||
|
||||
;; The font family is my design: <https://github.com/protesilaos/aporetic>.
|
||||
(setq fontaine-presets
|
||||
'((small
|
||||
:default-family "Iosevka Comfy Motion"
|
||||
:default-height 80
|
||||
:variable-pitch-family "Iosevka Comfy Duo")
|
||||
:default-height 80)
|
||||
(regular) ; like this it uses all the fallback values and is named `regular'
|
||||
(medium
|
||||
:default-weight semilight
|
||||
:default-family "Aporetic Serif Mono"
|
||||
:default-height 115
|
||||
:bold-weight extrabold)
|
||||
:fixed-pitch-family "Aporetic Serif Mono"
|
||||
:variable-pitch-family "Aporetic Sans")
|
||||
(large
|
||||
:inherit medium
|
||||
:default-height 150)
|
||||
(live-stream
|
||||
:default-family "Iosevka Comfy Wide Motion"
|
||||
:default-height 150
|
||||
:default-weight medium
|
||||
:fixed-pitch-family "Iosevka Comfy Wide Motion"
|
||||
:variable-pitch-family "Iosevka Comfy Wide Duo"
|
||||
:bold-weight extrabold)
|
||||
(presentation
|
||||
:default-height 180)
|
||||
:default-height 220)
|
||||
(jumbo
|
||||
:inherit medium
|
||||
:default-height 280)
|
||||
(t
|
||||
;; I keep all properties for didactic purposes, but most can be
|
||||
;; omitted. See the fontaine manual for the technicalities:
|
||||
;; <https://protesilaos.com/emacs/fontaine>.
|
||||
:default-family "Iosevka Comfy"
|
||||
:default-family "Aporetic Sans Mono"
|
||||
:default-weight regular
|
||||
:default-slant normal
|
||||
:default-width normal
|
||||
:default-height 100
|
||||
|
||||
:fixed-pitch-family "Iosevka Comfy"
|
||||
:fixed-pitch-family "Aporetic Sans Mono"
|
||||
:fixed-pitch-weight nil
|
||||
:fixed-pitch-slant nil
|
||||
:fixed-pitch-width nil
|
||||
:fixed-pitch-height 1.0
|
||||
|
||||
:fixed-pitch-serif-family nil
|
||||
:fixed-pitch-serif-weight nil
|
||||
:fixed-pitch-serif-slant nil
|
||||
:fixed-pitch-serif-width nil
|
||||
:fixed-pitch-serif-height 1.0
|
||||
|
||||
:variable-pitch-family "Iosevka Comfy Motion Duo"
|
||||
:variable-pitch-family "Aporetic Serif"
|
||||
:variable-pitch-weight nil
|
||||
:variable-pitch-slant nil
|
||||
:variable-pitch-width nil
|
||||
:variable-pitch-height 1.0
|
||||
|
||||
:mode-line-active-family nil
|
||||
:mode-line-active-weight nil
|
||||
:mode-line-active-slant nil
|
||||
:mode-line-active-width nil
|
||||
:mode-line-active-height 1.0
|
||||
|
||||
:mode-line-inactive-family nil
|
||||
:mode-line-inactive-weight nil
|
||||
:mode-line-inactive-slant nil
|
||||
:mode-line-inactive-width nil
|
||||
:mode-line-inactive-height 1.0
|
||||
|
||||
:header-line-family nil
|
||||
:header-line-weight nil
|
||||
:header-line-slant nil
|
||||
:header-line-width nil
|
||||
:header-line-height 1.0
|
||||
|
||||
:line-number-family nil
|
||||
:line-number-weight nil
|
||||
:line-number-slant nil
|
||||
:line-number-width nil
|
||||
:line-number-height 1.0
|
||||
|
||||
:tab-bar-family nil
|
||||
:tab-bar-weight nil
|
||||
:tab-bar-slant nil
|
||||
:tab-bar-width nil
|
||||
:tab-bar-height 1.0
|
||||
|
||||
:tab-line-family nil
|
||||
:tab-line-weight nil
|
||||
:tab-line-slant nil
|
||||
:tab-line-width nil
|
||||
:tab-line-height 1.0
|
||||
|
||||
:bold-family nil
|
||||
:bold-weight bold
|
||||
:bold-slant nil
|
||||
:bold-weight bold
|
||||
:bold-width nil
|
||||
:bold-height 1.0
|
||||
|
||||
:italic-family nil
|
||||
:italic-weight nil
|
||||
:italic-slant italic
|
||||
:italic-width nil
|
||||
:italic-height 1.0
|
||||
|
||||
:line-spacing nil)))
|
||||
|
||||
;; Set last preset or fall back to desired style from `fontaine-presets'.
|
||||
(fontaine-set-preset (or (fontaine-restore-latest-preset) 'regular))
|
||||
|
||||
;; Persist the latest font preset when closing/starting Emacs and
|
||||
;; while switching between themes.
|
||||
(fontaine-mode 1)
|
||||
|
||||
(with-eval-after-load 'pulsar
|
||||
(add-hook 'fontaine-set-preset-hook #'pulsar-pulse-line)))
|
||||
|
||||
;;;; Show Font (preview fonts)
|
||||
;; Read the manual: <https://protesilaos.com/emacs/show-font>
|
||||
(when prot-display-graphic-p
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install show-font)
|
||||
|
||||
(setq show-font-display-buffer-action-alist '(display-buffer-full-frame))
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"C-c S s" #'show-font-select-preview
|
||||
"C-c S l" #'show-font-tabulated)))
|
||||
|
||||
;;;;; `variable-pitch-mode' setup
|
||||
(use-package face-remap
|
||||
:ensure nil
|
||||
:functions prot/enable-variable-pitch
|
||||
:bind ( :map ctl-x-x-map
|
||||
("v" . variable-pitch-mode))
|
||||
:hook ((text-mode notmuch-show-mode elfeed-show-mode) . prot/enable-variable-pitch)
|
||||
:config
|
||||
;; NOTE 2022-11-20: This may not cover every case, though it works
|
||||
;; fine in my workflow. I am still undecided by EWW.
|
||||
(prot-emacs-configure
|
||||
(define-key ctl-x-x-map (kbd "v") #'variable-pitch-mode)
|
||||
|
||||
(defun prot/enable-variable-pitch ()
|
||||
(unless (derived-mode-p 'mhtml-mode 'nxml-mode 'yaml-mode)
|
||||
(variable-pitch-mode 1)))
|
||||
(when (bound-and-true-p modus-themes-mixed-fonts)
|
||||
(variable-pitch-mode 1))))
|
||||
|
||||
;; NOTE 2022-11-20: This may not cover every case, though it works
|
||||
;; fine in my workflow. I am still undecided by EWW.
|
||||
(prot-emacs-hook
|
||||
(text-mode-hook notmuch-show-mode-hook elfeed-show-mode-hook)
|
||||
prot/enable-variable-pitch)
|
||||
;;;;; Resize keys with global effect
|
||||
:bind
|
||||
|
||||
;; Emacs 29 introduces commands that resize the font across all
|
||||
;; buffers (including the minibuffer), which is what I want, as
|
||||
;; opposed to doing it only in the current buffer. The keys are the
|
||||
;; same as the defaults.
|
||||
(("C-x C-=" . global-text-scale-adjust)
|
||||
("C-x C-+" . global-text-scale-adjust)
|
||||
("C-x C-0" . global-text-scale-adjust)))
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x C-=" #'global-text-scale-adjust
|
||||
"C-x C-+" #'global-text-scale-adjust
|
||||
"C-x C-0" #'global-text-scale-adjust))
|
||||
|
||||
(provide 'prot-emacs-theme)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
;;; Vertical completion layout (vertico)
|
||||
(use-package vertico
|
||||
:ensure t
|
||||
:hook (after-init . vertico-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install vertico)
|
||||
|
||||
(setq vertico-scroll-margin 0)
|
||||
(setq vertico-count 5)
|
||||
(setq vertico-count 10)
|
||||
(setq vertico-resize t)
|
||||
(setq vertico-cycle t)
|
||||
|
||||
|
|
@ -13,42 +12,55 @@
|
|||
;; a sub-directory and use, say, `find-file' to go to your home '~/'
|
||||
;; or root '/' directory, Vertico will clear the old path to keep
|
||||
;; only your current input.
|
||||
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy)))
|
||||
(add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy))
|
||||
|
||||
(setq vertico-group-format
|
||||
(concat
|
||||
(propertize (make-string 20 ? ) 'face 'completions-group-separator)
|
||||
(propertize " %s " 'face 'completions-group-title)
|
||||
(propertize " " 'face 'completions-group-separator 'display '(space :align-to right))))
|
||||
|
||||
(vertico-mode 1))
|
||||
|
||||
;;; Custom tweaks for vertico (prot-vertico.el)
|
||||
(use-package prot-vertico
|
||||
:ensure nil
|
||||
:demand t
|
||||
:after vertico
|
||||
:bind
|
||||
( :map vertico-map
|
||||
("<left>" . backward-char)
|
||||
("<right>" . forward-char)
|
||||
("TAB" . prot-vertico-private-complete)
|
||||
("DEL" . vertico-directory-delete-char)
|
||||
("M-DEL" . vertico-directory-delete-word)
|
||||
("M-," . vertico-quick-insert)
|
||||
("M-." . vertico-quick-exit)
|
||||
:map vertico-multiform-map
|
||||
("C-n" . prot-vertico-private-next)
|
||||
("<down>" . prot-vertico-private-next)
|
||||
("C-p" . prot-vertico-private-previous)
|
||||
("<up>" . prot-vertico-private-previous)
|
||||
("C-l" . vertico-multiform-vertical))
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(require 'prot-vertico)
|
||||
(setq vertico-reverse-candidates nil)
|
||||
(setq vertico-multiform-commands
|
||||
`(("consult-\\(.*\\)?\\(find\\|grep\\|ripgrep\\)" ,@prot-vertico-multiform-maximal)))
|
||||
(setq vertico-multiform-categories
|
||||
`(;; Maximal
|
||||
(embark-keybinding ,@prot-vertico-multiform-maximal)
|
||||
(multi-category ,@prot-vertico-multiform-maximal)
|
||||
(consult-location ,@prot-vertico-multiform-maximal)
|
||||
(jinx ,@prot-vertico-multiform-maximal)
|
||||
(imenu ,@prot-vertico-multiform-maximal)
|
||||
(theme ,@prot-vertico-multiform-maximal)
|
||||
(unicode-name ,@prot-vertico-multiform-maximal)
|
||||
;; Minimal
|
||||
(file ,@prot-vertico-multiform-minimal
|
||||
(vertico-preselect . prompt)
|
||||
(vertico-sort-function . prot-vertico-sort-directories-first))
|
||||
(vertico-sort-function . vertico-sort-directories-first))
|
||||
(t ,@prot-vertico-multiform-minimal)))
|
||||
|
||||
(vertico-multiform-mode 1))
|
||||
(vertico-multiform-mode 1)
|
||||
|
||||
(prot-emacs-keybind vertico-map
|
||||
"<left>" #'backward-char
|
||||
"<right>" #'forward-char
|
||||
"<next>" #'vertico-next-group
|
||||
"<prior>" #'vertico-previous-group
|
||||
"TAB" #'prot-vertico-private-complete
|
||||
"DEL" #'vertico-directory-delete-char
|
||||
"M-DEL" #'vertico-directory-delete-word
|
||||
"M-," #'vertico-quick-insert
|
||||
"M-." #'vertico-quick-exit)
|
||||
(prot-emacs-keybind vertico-multiform-map
|
||||
"RET" #'prot-vertico-private-exit
|
||||
"<return>" #'prot-vertico-private-exit
|
||||
"C-n" #'prot-vertico-private-next
|
||||
"<down>" #'prot-vertico-private-next
|
||||
"C-p" #'prot-vertico-private-previous
|
||||
"<up>" #'prot-vertico-private-previous
|
||||
"C-l" #'vertico-multiform-vertical))
|
||||
|
||||
(provide 'prot-emacs-vertico)
|
||||
|
|
|
|||
|
|
@ -1,188 +1,166 @@
|
|||
;;;; `browse-url'
|
||||
(use-package browse-url
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq browse-url-browser-function 'eww-browse-url)
|
||||
(setq browse-url-secondary-browser-function 'browse-url-default-browser))
|
||||
|
||||
;;;; `goto-addr'
|
||||
(use-package goto-addr
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(defun prot/goto-address-mode ()
|
||||
"Enable `goto-address-mode' only in `text-mode' and no derivatives."
|
||||
(when (eq major-mode 'text-mode)
|
||||
(goto-address-mode 1)))
|
||||
|
||||
(add-hook 'text-mode-hook #'prot/goto-address-mode)
|
||||
(add-hook 'prog-mode-hook #'goto-address-prog-mode)
|
||||
(setq goto-address-url-face 'link)
|
||||
(setq goto-address-url-mouse-face 'highlight)
|
||||
(setq goto-address-mail-face nil)
|
||||
(setq goto-address-mail-mouse-face 'highlight))
|
||||
|
||||
;;;; `shr' (Simple HTML Renderer)
|
||||
(use-package shr
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq shr-use-colors nil) ; t is bad for accessibility
|
||||
(setq shr-use-fonts nil) ; t is not for me
|
||||
(setq shr-use-fonts nil) ; t is superfluous, given `variable-pitch-mode'
|
||||
(setq shr-max-image-proportion 0.6)
|
||||
(setq shr-image-animate nil) ; No GIFs, thank you!
|
||||
(setq shr-width fill-column) ; check `prot-eww-readable'
|
||||
(setq shr-width fill-column)
|
||||
(setq shr-max-width fill-column)
|
||||
(setq shr-discard-aria-hidden t)
|
||||
(setq shr-fill-text nil) ; Emacs 31
|
||||
(setq shr-cookie-policy nil))
|
||||
|
||||
;;;; `url-cookie'
|
||||
(use-package url-cookie
|
||||
:ensure nil
|
||||
:defer t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq url-cookie-untrusted-urls '(".*")))
|
||||
|
||||
;;;; `eww' (Emacs Web Wowser)
|
||||
(use-package eww
|
||||
:ensure nil
|
||||
:commands (eww)
|
||||
:bind
|
||||
( :map eww-link-keymap
|
||||
("v" . nil) ; stop overriding `eww-view-source'
|
||||
:map eww-mode-map
|
||||
("L" . eww-list-bookmarks)
|
||||
:map dired-mode-map
|
||||
("E" . eww-open-file) ; to render local HTML files
|
||||
:map eww-buffers-mode-map
|
||||
("d" . eww-bookmark-kill) ; it actually deletes
|
||||
:map eww-bookmark-mode-map
|
||||
("d" . eww-bookmark-kill)) ; same
|
||||
:config
|
||||
(setq eww-restore-desktop t)
|
||||
(setq eww-desktop-remove-duplicates t)
|
||||
(setq eww-header-line-format nil)
|
||||
(setq eww-search-prefix "https://duckduckgo.com/html/?q=")
|
||||
(setq eww-download-directory (expand-file-name "~/Documents/eww-downloads"))
|
||||
(setq eww-suggest-uris
|
||||
'(eww-links-at-point
|
||||
thing-at-point-url-at-point))
|
||||
(setq eww-bookmarks-directory (locate-user-emacs-file "eww-bookmarks/"))
|
||||
(setq eww-history-limit 150)
|
||||
(setq eww-use-external-browser-for-content-type
|
||||
"\\`\\(video/\\|audio\\)") ; On GNU/Linux check your mimeapps.list
|
||||
(setq eww-browse-url-new-window-is-tab nil)
|
||||
(setq eww-form-checkbox-selected-symbol "[X]")
|
||||
(setq eww-form-checkbox-symbol "[ ]")
|
||||
;; NOTE `eww-retrieve-command' is for Emacs28. I tried the following
|
||||
;; two values. The first would not render properly some plain text
|
||||
;; pages, such as by messing up the spacing between paragraphs. The
|
||||
;; second is more reliable but feels slower. So I just use the
|
||||
;; default (nil), though I find wget to be a bit faster. In that case
|
||||
;; one could live with the occasional errors by using `eww-download'
|
||||
;; on the offending page, but I prefer consistency.
|
||||
;;
|
||||
;; '("wget" "--quiet" "--output-document=-")
|
||||
;; '("chromium" "--headless" "--dump-dom")
|
||||
(setq eww-retrieve-command nil))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'eww
|
||||
(autoload #'prot-simple-buffers-major-mode "prot-simple")
|
||||
|
||||
(prot-emacs-keybind eww-mode-map
|
||||
"S" nil ; unmap `eww-list-buffers'
|
||||
"b" #'prot-simple-buffers-major-mode ; a general version to show buffer of current mode
|
||||
"m" #'bookmark-set)
|
||||
|
||||
(define-key eww-link-keymap (kbd "v") nil) ; stop overriding `eww-view-source'
|
||||
|
||||
(with-eval-after-load 'dired
|
||||
(define-key dired-mode-map (kbd "E") #'eww-open-file)) ; to render local HTML files
|
||||
|
||||
(setq eww-auto-rename-buffer 'title)
|
||||
(setq eww-header-line-format nil)
|
||||
(setq eww-bookmarks-directory (locate-user-emacs-file "eww-bookmarks/"))
|
||||
(setq eww-history-limit 150)
|
||||
(setq eww-use-external-browser-for-content-type
|
||||
"\\`\\(video/\\|audio\\)") ; On GNU/Linux check your mimeapps.list
|
||||
(setq eww-form-checkbox-selected-symbol "[X]")
|
||||
(setq eww-form-checkbox-symbol "[ ]")
|
||||
;; NOTE `eww-retrieve-command' is for Emacs28. I tried the following
|
||||
;; two values. The first would not render properly some plain text
|
||||
;; pages, such as by messing up the spacing between paragraphs. The
|
||||
;; second is more reliable but feels slower. So I just use the
|
||||
;; default (nil), though I find wget to be a bit faster. In that case
|
||||
;; one could live with the occasional errors by using `eww-download'
|
||||
;; on the offending page, but I prefer consistency.
|
||||
;;
|
||||
;; '("wget" "--quiet" "--output-document=-")
|
||||
;; '("chromium" "--headless" "--dump-dom")
|
||||
(setq eww-retrieve-command nil)
|
||||
|
||||
;; NOTE 2025-02-15: Emacs has a robust framework for writing
|
||||
;; bookmarks, which `eww' supports. Though `eww' also defines its
|
||||
;; own parallel bookmark data, which I do not want to use. So here
|
||||
;; I disable all the relevant commands.
|
||||
(dolist (command '( eww-list-bookmarks eww-add-bookmark eww-bookmark-mode
|
||||
eww-list-buffers eww-toggle-fonts eww-toggle-colors
|
||||
eww-switch-to-buffer))
|
||||
(put command 'disabled t))))
|
||||
|
||||
;;;; `prot-eww' extras
|
||||
(use-package prot-eww
|
||||
:ensure nil
|
||||
:after eww
|
||||
:config
|
||||
(setq prot-eww-save-history-file
|
||||
(locate-user-emacs-file "prot-eww-visited-history"))
|
||||
(setq prot-eww-save-visited-history t)
|
||||
(setq prot-eww-bookmark-link nil)
|
||||
|
||||
(add-hook 'prot-eww-history-mode-hook #'hl-line-mode)
|
||||
|
||||
(define-prefix-command 'prot-eww-map)
|
||||
(define-key global-map (kbd "C-c w") 'prot-eww-map)
|
||||
(prot-emacs-keybind prot-eww-map
|
||||
"b" #'prot-eww-visit-bookmark
|
||||
"e" #'prot-eww-browse-dwim
|
||||
"s" #'prot-eww-search-engine)
|
||||
(prot-emacs-keybind eww-mode-map
|
||||
"B" #'prot-eww-bookmark-page
|
||||
"D" #'prot-eww-download-html
|
||||
"F" #'prot-eww-find-feed
|
||||
"H" #'prot-eww-list-history
|
||||
"b" #'prot-eww-visit-bookmark
|
||||
"e" #'prot-eww-browse-dwim
|
||||
"o" #'prot-eww-open-in-other-window
|
||||
"E" #'prot-eww-visit-url-on-page
|
||||
"J" #'prot-eww-jump-to-url-on-page
|
||||
"R" #'prot-eww-readable
|
||||
"Q" #'prot-eww-quit))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'eww
|
||||
(require 'prot-eww)
|
||||
(prot-emacs-keybind eww-mode-map
|
||||
"F" #'prot-eww-find-feed
|
||||
"o" #'prot-eww-open-in-other-window
|
||||
"j" #'prot-eww-jump-to-url-on-page
|
||||
"J" #'prot-eww-visit-url-on-page)))
|
||||
|
||||
;;; Elfeed feed/RSS reader
|
||||
(use-package elfeed
|
||||
:ensure t
|
||||
:bind
|
||||
("C-c e" . elfeed)
|
||||
:config
|
||||
(setq elfeed-use-curl nil)
|
||||
(setq elfeed-curl-max-connections 10)
|
||||
(setq elfeed-db-directory (concat user-emacs-directory "elfeed/"))
|
||||
(setq elfeed-enclosure-default-dir "~/Downloads/")
|
||||
(setq elfeed-search-filter "@2-weeks-ago +unread")
|
||||
(setq elfeed-sort-order 'descending)
|
||||
(setq elfeed-search-clipboard-type 'CLIPBOARD)
|
||||
(setq elfeed-search-title-max-width 100)
|
||||
(setq elfeed-search-title-min-width 30)
|
||||
(setq elfeed-search-trailing-width 25)
|
||||
(setq elfeed-show-truncate-long-urls t)
|
||||
(setq elfeed-show-unique-buffers t)
|
||||
(setq elfeed-search-date-format '("%F %R" 16 :left))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install elfeed)
|
||||
|
||||
;; Make sure to also check the section on shr and eww for how I handle
|
||||
;; `shr-width' there.
|
||||
(add-hook 'elfeed-show-mode-hook
|
||||
(lambda () (setq-local shr-width (current-fill-column))))
|
||||
(define-key global-map (kbd "C-c e") #'elfeed)
|
||||
|
||||
(prot-emacs-keybind elfeed-search-mode-map
|
||||
"w" #'elfeed-search-yank
|
||||
"g" #'elfeed-update
|
||||
"G" #'elfeed-search-update--force)
|
||||
(with-eval-after-load 'elfeed
|
||||
|
||||
(define-key elfeed-show-mode-map (kbd "w") #'elfeed-show-yank))
|
||||
(add-hook 'elfeed-show-mode-hook #'visual-line-mode)
|
||||
|
||||
(use-package prot-elfeed
|
||||
:ensure nil
|
||||
:after elfeed
|
||||
:bind
|
||||
( :map elfeed-search-mode-map
|
||||
("s" . prot-elfeed-search-tag-filter)
|
||||
("+" . prot-elfeed-toggle-tag)
|
||||
:map elfeed-show-mode-map
|
||||
("+" . prot-elfeed-toggle-tag))
|
||||
:hook
|
||||
(elfeed-search-mode . prot-elfeed-load-feeds)
|
||||
:config
|
||||
(setq prot-elfeed-tag-faces t)
|
||||
(prot-elfeed-fontify-tags))
|
||||
(prot-emacs-keybind elfeed-search-mode-map
|
||||
"w" #'elfeed-search-yank
|
||||
"g" #'elfeed-update
|
||||
"G" #'revert-buffer)
|
||||
|
||||
(define-key elfeed-show-mode-map (kbd "w") #'elfeed-show-yank)
|
||||
|
||||
(setq elfeed-last-update-relative 0) ; absolute time in the header-line
|
||||
(setq elfeed-search-separator-date-format nil) ; no date sections in the search buffer
|
||||
(setq elfeed-use-curl nil)
|
||||
(setq elfeed-curl-max-connections 10)
|
||||
(setq elfeed-db-directory (expand-file-name "elfeed/" user-emacs-directory))
|
||||
(setq elfeed-enclosure-default-dir (expand-file-name "~/Downloads/"))
|
||||
(setq-default elfeed-search-filter "@2-weeks-ago +unread") ; NOTE 2026-05-28: was changed to buffer-local
|
||||
(setq elfeed-sort-order 'descending)
|
||||
(setq elfeed-search-clipboard-type 'CLIPBOARD)
|
||||
(setq elfeed-search-title-max-width 100)
|
||||
(setq elfeed-search-title-min-width 30)
|
||||
(setq elfeed-search-trailing-width 25)
|
||||
(setq elfeed-show-truncate-long-urls t)
|
||||
(setq elfeed-show-unique-buffers t)
|
||||
(setq elfeed-search-date-format '("%F %R" 20 :left))
|
||||
;; NOTE 2025-10-28: Instead of showing my `elfeed-feeds' here, I put
|
||||
;; it in its own Emacs Lisp file. The `load' function will evaluate
|
||||
;; that code. Internally, the file has just the following:
|
||||
;;
|
||||
;; (setq elfeed-feeds FEEDS)
|
||||
(unless (bound-and-true-p elfeed-feeds)
|
||||
(load (expand-file-name "~/feeds.el.gpg") :no-error :no-message))
|
||||
;; Make entries tagged as "personal" use the `bold-italic' face.
|
||||
;; This way I can quickly notice my own entries in the listing.
|
||||
(add-to-list 'elfeed-search-face-alist '(personal bold-italic))
|
||||
|
||||
;; NOTE 2026-05-29: Make `elfeed-search-yank' mark the entry as
|
||||
;; read. If I am copying the URL, then it means I already plan to
|
||||
;; do something with this entry outside of Elfeed.
|
||||
(define-advice elfeed-search-yank (:around (&rest args) prot)
|
||||
(save-excursion
|
||||
(apply args))
|
||||
(call-interactively 'elfeed-search-untag-all-unread))))
|
||||
|
||||
;;; Rcirc (IRC client)
|
||||
(use-package rcirc
|
||||
:ensure nil
|
||||
:bind ("C-c i" . irc)
|
||||
:config
|
||||
(setq rcirc-server-alist
|
||||
`(("irc.libera.chat"
|
||||
:channels ("#emacs" "#rcirc")
|
||||
:port 6697
|
||||
:encryption tls
|
||||
:password ,(prot-common-auth-get-field "libera" :secret))))
|
||||
(prot-emacs-configure
|
||||
(with-eval-after-load 'rcirc
|
||||
(setq rcirc-server-alist
|
||||
`(("irc.libera.chat"
|
||||
:channels ("#emacs")
|
||||
:port 6697
|
||||
:encryption tls
|
||||
:password ,(prot-common-auth-get-field "libera" :secret))))
|
||||
|
||||
(setq rcirc-prompt "%t> ") ; Read the docs or use (customize-set-variable 'rcirc-prompt "%t> ")
|
||||
(setq rcirc-prompt "%t> ") ; Read the docs or use (customize-set-variable 'rcirc-prompt "%t> ")
|
||||
|
||||
(setq rcirc-default-nick "protesilaos"
|
||||
rcirc-default-user-name rcirc-default-nick
|
||||
rcirc-default-full-name "Protesilaos Stavrou")
|
||||
(setq rcirc-default-nick "protesilaos"
|
||||
rcirc-default-user-name "protesilaos"
|
||||
rcirc-default-full-name "Protesilaos")
|
||||
|
||||
;; ;; NOTE 2021-11-28: demo from the days of EmacsConf 2021. I don't
|
||||
;; ;; actually need this.
|
||||
;; (setq rcirc-bright-nicks '("bandali" "sachac" "zaeph"))
|
||||
;; ;; NOTE 2021-11-28: demo from the days of EmacsConf 2021. I don't
|
||||
;; ;; actually need this.
|
||||
;; (setq rcirc-bright-nicks '("bandali" "sachac" "zaeph"))
|
||||
|
||||
;; NOTE 2021-11-28: Is there a canonical way to disable this?
|
||||
(setq rcirc-timeout-seconds most-positive-fixnum)
|
||||
;; NOTE 2021-11-28: Is there a canonical way to disable this?
|
||||
(setq rcirc-timeout-seconds most-positive-fixnum)
|
||||
|
||||
(rcirc-track-minor-mode 1))
|
||||
(rcirc-track-minor-mode 1)))
|
||||
|
||||
(provide 'prot-emacs-web)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
(use-package which-key
|
||||
:ensure t
|
||||
:hook (after-init . which-key-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq which-key-separator " ")
|
||||
(setq which-key-prefix-prefix "... ")
|
||||
(setq which-key-max-display-columns 3)
|
||||
(setq which-key-idle-delay 1.5)
|
||||
(setq which-key-idle-secondary-delay 0.25)
|
||||
(setq which-key-add-column-padding 1)
|
||||
(setq which-key-max-description-length 40))
|
||||
(setq which-key-max-description-length 40)
|
||||
|
||||
(which-key-mode 1)) ; built into Emacs 30
|
||||
|
||||
(provide 'prot-emacs-which-key)
|
||||
|
|
|
|||
|
|
@ -1,197 +1,213 @@
|
|||
(prot-emacs-configure
|
||||
(setq tab-bar-new-button-show nil)
|
||||
(setq tab-bar-close-button-show nil)
|
||||
(setq tab-bar-show 1)
|
||||
|
||||
(advice-add #'window--delete :after #'prot-common-clear-minibuffer-message))
|
||||
|
||||
;;; General window and buffer configurations
|
||||
(use-package uniquify
|
||||
:ensure nil
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
;;;; `uniquify' (unique names for buffers)
|
||||
(setq uniquify-buffer-name-style 'forward)
|
||||
(setq uniquify-strip-common-suffix t)
|
||||
(setq uniquify-after-kill-buffer-p t))
|
||||
|
||||
;;;; Line highlight
|
||||
(use-package hl-line
|
||||
:ensure nil
|
||||
:commands (hl-line-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq hl-line-sticky-flag nil)
|
||||
(setq hl-line-overlay-priority -50)) ; emacs28
|
||||
|
||||
;;;; Negative space highlight
|
||||
(use-package whitespace
|
||||
:ensure nil
|
||||
:bind
|
||||
(("<f6>" . whitespace-mode)
|
||||
("C-c z" . delete-trailing-whitespace))
|
||||
:config
|
||||
;; NOTE 2023-08-14: This is experimental. I am not sure I like it.
|
||||
(prot-emacs-configure
|
||||
(setq whitespace-style
|
||||
'(face
|
||||
tabs
|
||||
spaces
|
||||
newline
|
||||
tab-mark
|
||||
space-mark
|
||||
newline-mark
|
||||
trailing
|
||||
missing-newline-at-eof
|
||||
space-after-tab::tab
|
||||
space-after-tab::space
|
||||
space-before-tab::tab
|
||||
space-before-tab::space)))
|
||||
space-before-tab::space))
|
||||
|
||||
(prot-emacs-keybind global-map
|
||||
"<f6>" #'whitespace-mode
|
||||
"C-c z" #'delete-trailing-whitespace))
|
||||
|
||||
;;; Line numbers on the side of the window
|
||||
(use-package display-line-numbers
|
||||
:ensure nil
|
||||
:bind
|
||||
("<f7>" . display-line-numbers-mode)
|
||||
:config
|
||||
(setq-default display-line-numbers-type t)
|
||||
(prot-emacs-configure
|
||||
(setq display-line-numbers-type t)
|
||||
;; Those two variables were introduced in Emacs 27.1
|
||||
(setq display-line-numbers-major-tick 0)
|
||||
(setq display-line-numbers-minor-tick 0)
|
||||
;; Use absolute numbers in narrowed buffers
|
||||
(setq-default display-line-numbers-widen t))
|
||||
|
||||
;;;; `window', `display-buffer-alist', and related
|
||||
(use-package prot-window
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
;; NOTE 2023-03-17: Remember that I am using development versions of
|
||||
;; Emacs. Some of my `display-buffer-alist' contents are for Emacs
|
||||
;; 29+.
|
||||
(setq display-buffer-alist
|
||||
`(;; no window
|
||||
("\\`\\*Async Shell Command\\*\\'"
|
||||
(display-buffer-no-window))
|
||||
("\\`\\*\\(Warnings\\|Compile-Log\\|Org Links\\)\\*\\'"
|
||||
(display-buffer-no-window)
|
||||
(allow-no-window . t))
|
||||
;; bottom side window
|
||||
("\\*Org \\(Select\\|Note\\)\\*" ; the `org-capture' key selection and `org-add-log-note'
|
||||
(display-buffer-in-side-window)
|
||||
(dedicated . t)
|
||||
(side . bottom)
|
||||
(slot . 0)
|
||||
(window-parameters . ((mode-line-format . none))))
|
||||
;; bottom buffer (NOT side window)
|
||||
((or . ((derived-mode . flymake-diagnostics-buffer-mode)
|
||||
(derived-mode . flymake-project-diagnostics-mode)
|
||||
(derived-mode . messages-buffer-mode)
|
||||
(derived-mode . backtrace-mode)))
|
||||
(display-buffer-reuse-mode-window display-buffer-at-bottom)
|
||||
(window-height . 0.3)
|
||||
(dedicated . t)
|
||||
(preserve-size . (t . t)))
|
||||
("\\*Embark Actions\\*"
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(window-height . fit-window-to-buffer)
|
||||
(window-parameters . ((no-other-window . t)
|
||||
(mode-line-format . none))))
|
||||
("\\*\\(Output\\|Register Preview\\).*"
|
||||
(display-buffer-reuse-mode-window display-buffer-at-bottom))
|
||||
;; below current window
|
||||
("\\(\\*Capture\\*\\|CAPTURE-.*\\)"
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected))
|
||||
("\\*\\vc-\\(incoming\\|outgoing\\|git : \\).*"
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(window-height . 0.1)
|
||||
(dedicated . t)
|
||||
(preserve-size . (t . t)))
|
||||
((derived-mode . reb-mode) ; M-x re-builder
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(window-height . 4) ; note this is literal lines, not relative
|
||||
(dedicated . t)
|
||||
(preserve-size . (t . t)))
|
||||
((or . ((derived-mode . occur-mode)
|
||||
(derived-mode . grep-mode)
|
||||
(derived-mode . Buffer-menu-mode)
|
||||
(derived-mode . log-view-mode)
|
||||
(derived-mode . help-mode) ; See the hooks for `visual-line-mode'
|
||||
"\\*\\(|Buffer List\\|Occur\\|vc-change-log\\|eldoc.*\\).*"
|
||||
prot-window-shell-or-term-p
|
||||
;; ,world-clock-buffer-name
|
||||
))
|
||||
(prot-window-display-buffer-below-or-pop)
|
||||
(body-function . prot-window-select-fit-size))
|
||||
("\\*\\(Calendar\\|Bookmark Annotation\\|ert\\).*"
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(dedicated . t)
|
||||
(window-height . fit-window-to-buffer))
|
||||
;; NOTE 2022-09-10: The following is for `ispell-word', though
|
||||
;; it only works because I override `ispell-display-buffer'
|
||||
;; with `prot-spell-ispell-display-buffer' and change the
|
||||
;; value of `ispell-choices-buffer'.
|
||||
("\\*ispell-top-choices\\*.*"
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(window-height . fit-window-to-buffer))
|
||||
;; same window
|
||||
(prot-emacs-configure
|
||||
(setq prot-streaming-mode-modes '(text-mode prog-mode dired-mode comint-mode))
|
||||
(prot-emacs-autoload prot-streaming-mode "prot-streaming")
|
||||
(define-key global-map (kbd "<f7>") #'prot-streaming-mode))
|
||||
|
||||
;; NOTE 2023-02-17: `man' does not fully obey the
|
||||
;; `display-buffer-alist'. It works for new frames and for
|
||||
;; `display-buffer-below-selected', but otherwise is
|
||||
;; unpredictable. See `Man-notify-method'.
|
||||
((or . ((derived-mode . Man-mode)
|
||||
(derived-mode . woman-mode)
|
||||
"\\*\\(Man\\|woman\\).*"))
|
||||
(display-buffer-same-window)))))
|
||||
;;;; `window', `display-buffer-alist', and related
|
||||
(prot-emacs-configure
|
||||
(require 'prot-window)
|
||||
;; NOTE 2023-03-17: Remember that I am using development versions of
|
||||
;; Emacs. Some of my `display-buffer-alist' contents are for Emacs
|
||||
;; 29+.
|
||||
(setq display-buffer-alist
|
||||
`(;; no window
|
||||
("\\`\\*Async Shell Command\\*\\'"
|
||||
(display-buffer-no-window))
|
||||
("\\`\\*\\(Warnings\\|Compile-Log\\|Org Links\\)\\*\\'"
|
||||
(display-buffer-no-window)
|
||||
(allow-no-window . t))
|
||||
;; bottom side window
|
||||
("\\*\\(Org \\(Select\\|Note\\)\\|Agenda Commands\\)\\*" ; the `org-capture' key selection and `org-add-log-note'
|
||||
(display-buffer-in-side-window)
|
||||
(dedicated . t)
|
||||
(side . bottom)
|
||||
(slot . 0)
|
||||
(window-parameters . ((mode-line-format . none))))
|
||||
;; bottom buffer (NOT side window)
|
||||
((or . ((derived-mode . flymake-diagnostics-buffer-mode)
|
||||
(derived-mode . flymake-project-diagnostics-mode)
|
||||
(derived-mode . messages-buffer-mode)
|
||||
(derived-mode . backtrace-mode)))
|
||||
(display-buffer-reuse-mode-window display-buffer-at-bottom)
|
||||
(mode . ( flymake-diagnostics-buffer-mode flymake-project-diagnostics-mode
|
||||
messages-buffer-mode backtrace-mode))
|
||||
(inhibit-switch-frame . t)
|
||||
(window-height . 0.3)
|
||||
(dedicated . t)
|
||||
(preserve-size . (t . t))
|
||||
(body-function . select-window))
|
||||
("\\*Embark Actions\\*"
|
||||
;; NOTE 2026-07-14: I am including `display-buffer-at-bottom' here because Embark can be
|
||||
;; called from the minibuffer where `display-buffer-below-selected' does not work as intended.
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected display-buffer-at-bottom)
|
||||
(window-height . fit-window-to-buffer)
|
||||
(window-parameters . ((no-other-window . t)
|
||||
(mode-line-format . none))))
|
||||
("\\*\\(Output\\|Register Preview\\).*"
|
||||
(display-buffer-reuse-mode-window display-buffer-at-bottom)
|
||||
(inhibit-switch-frame . t))
|
||||
;; below current window
|
||||
("\\*prot-elisp-macroexpand\\*"
|
||||
(display-buffer-below-selected)
|
||||
(window-height . 0.3)
|
||||
(dedicated . t)
|
||||
(preserve-size . (t . t))
|
||||
(body-function . select-window))
|
||||
((derived-mode . reb-mode) ; M-x re-builder
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(inhibit-switch-frame . t)
|
||||
(window-height . 4) ; note this is literal lines, not relative
|
||||
(dedicated . t)
|
||||
(preserve-size . (t . t)))
|
||||
((or . ((derived-mode . occur-mode)
|
||||
(derived-mode . grep-mode)
|
||||
(derived-mode . Buffer-menu-mode)
|
||||
(derived-mode . log-view-mode)
|
||||
(derived-mode . help-mode) ; See the hooks for `visual-line-mode'
|
||||
"\\*\\(|Buffer List\\|Occur\\|vc-change-log\\|eldoc.*\\).*"))
|
||||
(prot-window-display-buffer-below-or-pop)
|
||||
(body-function . prot-window-select-fit-size))
|
||||
(prot-window-shell-or-term-p
|
||||
(display-buffer-reuse-mode-window display-buffer-at-bottom)
|
||||
(mode . (shell-mode eshell-mode comint-mode))
|
||||
(inhibit-switch-frame . t)
|
||||
(body-function . prot-window-select-fit-size))
|
||||
((derived-mode . vc-compilation-mode)
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(inhibit-switch-frame . t)
|
||||
(dedicated . t)
|
||||
(window-height . 0.1))
|
||||
((or . ((derived-mode . calendar-mode)
|
||||
(derived-mode . ert-results-mode)))
|
||||
(display-buffer-reuse-mode-window display-buffer-below-selected)
|
||||
(mode . (calendar-mode ert-results-mode))
|
||||
(inhibit-switch-frame . t)
|
||||
(dedicated . t)
|
||||
(window-height . fit-window-to-buffer))
|
||||
;; NOTE 2022-09-10: The following is for `ispell-word', though
|
||||
;; it only works because I override `ispell-display-buffer'
|
||||
;; with `prot-spell-ispell-display-buffer' and change the
|
||||
;; value of `ispell-choices-buffer'.
|
||||
("\\*ispell-top-choices\\*.*"
|
||||
(display-buffer-below-selected)
|
||||
(window-height . fit-window-to-buffer))
|
||||
;; current window
|
||||
((or . (;; NOTE 2023-02-17: `man' does not fully obey the
|
||||
;; `display-buffer-alist'. It works for new frames
|
||||
;; and for `display-buffer-below-selected', but
|
||||
;; otherwise is unpredictable. See `Man-notify-method'.
|
||||
(derived-mode . Man-mode)
|
||||
(derived-mode . woman-mode)
|
||||
"\\*\\(Man\\|woman\\).*"))
|
||||
(display-buffer-reuse-mode-window display-buffer-same-window))
|
||||
((or . ((derived-mode . magit-diff-mode)
|
||||
(derived-mode . magit-log-mode)))
|
||||
(display-buffer-reuse-mode-window prot-window-display-new display-buffer-pop-up-window)
|
||||
(inhibit-switch-frame . t))
|
||||
;; other `tab-bar-mode' tab
|
||||
((or . ((derived-mode . magit-status-mode)
|
||||
(derived-mode . vc-dir-mode)
|
||||
"\\*.*vc-dir.*\\*"))
|
||||
(display-buffer-reuse-mode-window display-buffer-in-tab)
|
||||
(mode . (magit-status-mode vc-dir-mode))
|
||||
(tab-name . (lambda (buffer _alist) (buffer-name buffer)))
|
||||
(reusable-frames . :just-the-selected-frame)
|
||||
(inhibit-switch-frame . t)))))
|
||||
|
||||
(use-package prot-window
|
||||
:ensure nil
|
||||
:demand t
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(setq split-window-preferred-direction 'horizontal) ; Emacs 31
|
||||
(setq window-combination-resize t)
|
||||
(setq even-window-sizes 'height-only)
|
||||
(setq window-sides-vertical nil)
|
||||
(setq switch-to-buffer-in-dedicated-window 'pop)
|
||||
(setq split-height-threshold 80)
|
||||
(setq split-height-threshold 85)
|
||||
(setq split-width-threshold 125)
|
||||
(setq window-min-height 3)
|
||||
(setq window-min-width 30))
|
||||
|
||||
(use-package prot-window
|
||||
:ensure nil
|
||||
:demand t
|
||||
:hook
|
||||
((epa-info-mode help-mode custom-mode) . visual-line-mode))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-hook (epa-info-mode-hook help-mode-hook custom-mode-hook) visual-line-mode))
|
||||
|
||||
(use-package prot-window
|
||||
:ensure nil
|
||||
:demand t
|
||||
:hook
|
||||
((world-clock-mode calendar-mode) . prot-common-truncate-lines-silently))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-hook (world-clock-mode-hook calendar-mode-hook) prot-common-truncate-lines-silently))
|
||||
|
||||
(use-package prot-window
|
||||
:ensure nil
|
||||
:demand t
|
||||
:bind
|
||||
( :map global-map
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-keybind global-map
|
||||
;; NOTE 2022-09-17: Also see `prot-simple-swap-window-buffers'.
|
||||
("C-x <down>" . next-buffer)
|
||||
("C-x <up>" . previous-buffer)
|
||||
("C-x C-n" . next-buffer) ; override `set-goal-column'
|
||||
("C-x C-p" . previous-buffer) ; override `mark-page'
|
||||
("C-x !" . delete-other-windows-vertically)
|
||||
("C-x _" . balance-windows) ; underscore
|
||||
("C-x -" . fit-window-to-buffer) ; hyphen
|
||||
("C-x +" . balance-windows-area)
|
||||
("C-x }" . enlarge-window)
|
||||
("C-x {" . shrink-window)
|
||||
("C-x >" . enlarge-window-horizontally) ; override `scroll-right'
|
||||
("C-x <" . shrink-window-horizontally) ; override `scroll-left'
|
||||
:map resize-window-repeat-map
|
||||
(">" . enlarge-window-horizontally)
|
||||
("<" . shrink-window-horizontally)))
|
||||
"C-x <down>" #'next-buffer
|
||||
"C-x <up>" #'previous-buffer
|
||||
"C-x C-n" #'next-buffer ; override `set-goal-column'
|
||||
"C-x C-p" #'previous-buffer ; override `mark-page'
|
||||
"C-x !" #'delete-other-windows-vertically
|
||||
"C-x _" #'balance-windows ; underscore
|
||||
"C-x -" #'fit-window-to-buffer ; hyphen
|
||||
"C-x +" #'balance-windows-area
|
||||
"C-x }" #'enlarge-window
|
||||
"C-x {" #'shrink-window
|
||||
"C-x >" #'enlarge-window-horizontally ; override `scroll-right'
|
||||
"C-x <" #'shrink-window-horizontally) ; override `scroll-left'
|
||||
(prot-emacs-keybind resize-window-repeat-map
|
||||
">" #'enlarge-window-horizontally
|
||||
"<" #'shrink-window-horizontally))
|
||||
|
||||
;;; Frame-isolated buffers
|
||||
;; Another package of mine. Read the manual:
|
||||
;; <https://protesilaos.com/emacs/beframe>.
|
||||
(use-package beframe
|
||||
:ensure t
|
||||
:hook (after-init . beframe-mode)
|
||||
:config
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-install beframe)
|
||||
(require 'beframe)
|
||||
|
||||
(setq beframe-functions-in-frames '(project-prompt-project-dir))
|
||||
|
||||
;; I use this instead of :bind because I am binding a keymap and the
|
||||
;; way `use-package' does it is by wrapping a lambda around it that
|
||||
;; then breaks `describe-key' for those keys.
|
||||
(prot-emacs-keybind global-map
|
||||
;; Override the `set-fill-column' that I have no use for.
|
||||
"C-x f" #'other-frame-prefix
|
||||
|
|
@ -204,47 +220,102 @@
|
|||
"C-x C-b" #'beframe-buffer-menu
|
||||
;; Not specific to Beframe, but since it renames frames (by means
|
||||
;; of `beframe-mode') it is appropriate to have this here:
|
||||
"C-x B" #'select-frame-by-name))
|
||||
"C-x B" #'select-frame-by-name)
|
||||
|
||||
(beframe-mode 1)
|
||||
|
||||
(with-eval-after-load 'consult
|
||||
(defface beframe-buffer
|
||||
'((t :inherit font-lock-string-face))
|
||||
"Face for `consult' framed buffers.")
|
||||
|
||||
(defun prot/beframe-buffer-names-sorted (&optional frame)
|
||||
"Return the list of buffers from `beframe-buffer-names' sorted by visibility.
|
||||
With optional argument FRAME, return the list of buffers of FRAME."
|
||||
(beframe-buffer-names frame :sort #'beframe-buffer-sort-visibility))
|
||||
|
||||
(defvar beframe-consult-source
|
||||
`( :name "Frame-specific buffers (current frame)"
|
||||
:narrow ?F
|
||||
:category buffer
|
||||
:face beframe-buffer
|
||||
:history beframe-history
|
||||
:items ,#'prot/beframe-buffer-names-sorted
|
||||
:action ,#'switch-to-buffer
|
||||
:state ,#'consult--buffer-state))
|
||||
|
||||
(add-to-list 'consult-buffer-sources 'beframe-consult-source)))
|
||||
|
||||
;;; Frame history (undelete-frame-mode)
|
||||
(use-package frame
|
||||
:ensure nil
|
||||
:bind ("C-x u" . undelete-frame) ; I use only C-/ for `undo'
|
||||
:hook (after-init . undelete-frame-mode))
|
||||
(prot-emacs-configure
|
||||
(define-key global-map (kbd "C-x u") #'undelete-frame) ; I use only C-/ for `undo'
|
||||
(undelete-frame-mode 1))
|
||||
|
||||
;;; Window history (winner-mode)
|
||||
(use-package winner
|
||||
:ensure nil
|
||||
:hook (after-init . winner-mode)
|
||||
:bind
|
||||
(("C-x <right>" . winner-redo)
|
||||
("C-x <left>" . winner-undo)))
|
||||
(prot-emacs-configure
|
||||
(prot-emacs-keybind global-map
|
||||
"C-x <right>" #'winner-redo
|
||||
"C-x <left>" #'winner-undo)
|
||||
|
||||
(winner-mode 1))
|
||||
|
||||
;;; Directional window motions (windmove)
|
||||
(use-package windmove
|
||||
:ensure nil
|
||||
:bind
|
||||
(prot-emacs-configure
|
||||
;; Those override some commands that are already available with
|
||||
;; C-M-u, C-M-f, C-M-b.
|
||||
(("C-M-<up>" . windmove-up)
|
||||
("C-M-<right>" . windmove-right)
|
||||
("C-M-<down>" . windmove-down)
|
||||
("C-M-<left>" . windmove-left)
|
||||
("C-M-S-<up>" . windmove-swap-states-up)
|
||||
("C-M-S-<right>" . windmove-swap-states-right) ; conflicts with `org-increase-number-at-point'
|
||||
("C-M-S-<down>" . windmove-swap-states-down)
|
||||
("C-M-S-<left>" . windmove-swap-states-left))
|
||||
:config
|
||||
(prot-emacs-keybind global-map
|
||||
"C-M-<up>" #'windmove-up
|
||||
"C-M-<right>" #'windmove-right
|
||||
"C-M-<down>" #'windmove-down
|
||||
"C-M-<left>" #'windmove-left
|
||||
"C-M-S-<up>" #'windmove-swap-states-up
|
||||
"C-M-S-<right>" #'windmove-swap-states-right ; conflicts with `org-increase-number-at-point'
|
||||
"C-M-S-<down>" #'windmove-swap-states-down
|
||||
"C-M-S-<left>" #'windmove-swap-states-left)
|
||||
|
||||
(setq windmove-create-window nil)) ; Emacs 27.1
|
||||
|
||||
;;; Header line context of symbol/heading (breadcrumb.el)
|
||||
(use-package breadcrumb
|
||||
:ensure t
|
||||
:hook (after-init . breadcrumb-mode)
|
||||
:config
|
||||
(setq breadcrumb-project-max-length 0.5)
|
||||
(setq breadcrumb-project-crumb-separator "/")
|
||||
(setq breadcrumb-imenu-max-length 1.0)
|
||||
(setq breadcrumb-imenu-crumb-separator " > "))
|
||||
;;; Buffer to PDF
|
||||
(prot-emacs-configure
|
||||
;; The use the command `buffer-to-pdf' or `buffer-to-pdf-black-on-white'.
|
||||
(prot-emacs-install-vc
|
||||
buffer-to-pdf
|
||||
"https://github.com/protesilaos/buffer-to-pdf.git"))
|
||||
|
||||
(prot-emacs-configure
|
||||
(setq speedbar-use-images nil) ; t here refers to the `cdr' of each `speedbar-expand-image-button-alist'
|
||||
(setq speedbar-smart-directory-expand-flag nil)
|
||||
(setq speedbar-prefer-window t ; both of these are for Emacs 31
|
||||
speedbar-window-max-width 60)
|
||||
(setq speedbar-indentation-width 4)
|
||||
(setq speedbar-show-unknown-files t)
|
||||
;; NOTE 2026-08-05 17:08 +0300: Waiting for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=81556 which I just submitted.
|
||||
(setq speedbar-directory-unshown-regexp nil
|
||||
speedbar-file-unshown-regexp nil)
|
||||
(prot-emacs-hook speedbar-visiting-tag-hook (speedbar-recenter speedbar-highlight-one-tag-line))
|
||||
|
||||
(defun prot/speedbar-imenu-flatten (elements)
|
||||
"Flatten the Imenu ELEMENTS."
|
||||
(let* ((elements (imenu--flatten-index-alist elements t))
|
||||
(elements-no-prefix (mapcar
|
||||
(lambda (element)
|
||||
(when-let* ((string (car element))
|
||||
(position (cdr element))
|
||||
(_ (or (natnump position) (markerp position))))
|
||||
(cond
|
||||
((string-match-p ":.*:" string)
|
||||
(cons
|
||||
(string-join (cdr (split-string string "[:]")))
|
||||
position))
|
||||
((string-match-p ":" string)
|
||||
(cons
|
||||
(cadr (split-string string "[:]"))
|
||||
position))
|
||||
(t
|
||||
element))))
|
||||
elements)))
|
||||
(delq nil elements-no-prefix)))
|
||||
|
||||
(setq speedbar-tag-hierarchy-method '(prot/speedbar-imenu-flatten)))
|
||||
|
||||
(provide 'prot-emacs-window)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-abbrev.el --- Functions for use with abbrev-mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2024 Protesilaos Stavrou
|
||||
;; Copyright (C) 2025-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -25,11 +25,6 @@
|
|||
;;; Commentary:
|
||||
;;
|
||||
;; Functions for use with `abbrev-mode'.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -75,5 +70,39 @@ Use completion among previous entries, retrieving their data from
|
|||
"Insert message to update NAME.html page, by prompting for NAME."
|
||||
(insert (format "Update %s.html" (prot-abbrev-update-html-prompt))))
|
||||
|
||||
(defvar prot-abbrev-org-macro-key-history nil
|
||||
"Minibuffer history for `prot-abbrev-org-macro-key-prompt'.")
|
||||
|
||||
(defun prot-abbrev-org-macro-key-prompt ()
|
||||
"Minibuffer prompt for `prot-abbrev-org-macro-key'.
|
||||
Use completion among previous entries, retrieving their data from
|
||||
`prot-abbrev-org-macro-key-history'."
|
||||
(completing-read
|
||||
"Key binding: "
|
||||
prot-abbrev-org-macro-key-history
|
||||
nil nil nil 'prot-abbrev-org-macro-key-history))
|
||||
|
||||
(defvar prot-abbrev-org-macro-key-symbol-history nil
|
||||
"Minibuffer history for `prot-abbrev-org-macro-key-symbol-prompt'.")
|
||||
|
||||
(defun prot-abbrev-org-macro-key-symbol-prompt ()
|
||||
"Minibuffer prompt for `prot-abbrev-org-macro-key'.
|
||||
Use completion among previous entries, retrieving their data from
|
||||
`prot-abbrev-org-macro-key-symbol-history'."
|
||||
(completing-read
|
||||
"Command name: "
|
||||
prot-abbrev-org-macro-key-symbol-history
|
||||
nil nil nil 'prot-abbrev-org-macro-key-symbol-history))
|
||||
|
||||
(defun prot-abbrev-org-macro-key-command ()
|
||||
"Insert {{{kbd(KEY)}}} (~SYMBOL~) by prompting for KEY and SYMBOL."
|
||||
(insert (format "{{{kbd(%s)}}} (~%s~)"
|
||||
(prot-abbrev-org-macro-key-prompt)
|
||||
(prot-abbrev-org-macro-key-symbol-prompt))))
|
||||
|
||||
(defun prot-abbrev-org-macro-key ()
|
||||
"Insert {{{kbd(KEY)}}} by prompting for KEY."
|
||||
(insert (format "{{{kbd(%s)}}}" (prot-abbrev-org-macro-key-prompt))))
|
||||
|
||||
(provide 'prot-abbrev)
|
||||
;;; prot-abbrev.el ends here
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-coach.el --- Code for my personal coaching sessions -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -43,11 +43,11 @@
|
|||
|
||||
(defun prot-coach--name-prompt-default ()
|
||||
"Return default value for `prot-coach-name-prompt'."
|
||||
(when-let ((from (cond
|
||||
((derived-mode-p 'message-mode)
|
||||
(message-fetch-field "To"))
|
||||
((derived-mode-p 'notmuch-show-mode)
|
||||
(notmuch-show-get-header :From)))))
|
||||
(when-let* ((from (cond
|
||||
((derived-mode-p 'message-mode)
|
||||
(message-fetch-field "To"))
|
||||
((derived-mode-p 'notmuch-show-mode)
|
||||
(notmuch-show-get-header :From)))))
|
||||
(string-clean-whitespace (car (split-string from "<")))))
|
||||
|
||||
(defun prot-coach-name-prompt ()
|
||||
|
|
@ -135,7 +135,7 @@ Omit entries with a CANCEL state."
|
|||
(erase-buffer)
|
||||
(goto-char (point-min))
|
||||
(insert "# Type C-c C-c in the TBLFM line to produce the table\n\n")
|
||||
(insert "* Cost counting without counting minutes\n")
|
||||
(insert "* Cost without counting minutes\n")
|
||||
(insert (concat
|
||||
"\n"
|
||||
"| Description | Started | Closed | Days | Hours | Cost (EUR) |" "\n"
|
||||
|
|
@ -172,7 +172,7 @@ Omit entries with a CANCEL state."
|
|||
(let (sessions)
|
||||
(org-map-entries
|
||||
(lambda ()
|
||||
(when-let ((entry (prot-coach--get-deadline-and-close name)))
|
||||
(when-let* ((entry (prot-coach--get-deadline-and-close name)))
|
||||
(push entry sessions))))
|
||||
(prot-coach--print-table-with-sessions name sessions))))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-comment.el --- Extensions newcomment.el for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2021-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my newcomment.el extras, for use in my Emacs setup:
|
||||
;; https://protesilaos.com/emacs/dotemacs.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-common.el --- Common functions for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2020-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2020-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -25,14 +25,11 @@
|
|||
;;; Commentary:
|
||||
;;
|
||||
;; Common functions for my Emacs: <https://protesilaos.com/emacs/dotemacs/>.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
;; TODO 2025-12-21: I need to review all this.
|
||||
|
||||
(eval-when-compile
|
||||
(require 'subr-x)
|
||||
(require 'cl-lib))
|
||||
|
|
@ -41,6 +38,24 @@
|
|||
"Auxiliary functions for my dotemacs."
|
||||
:group 'editing)
|
||||
|
||||
(defvar prot-common-file-extensions
|
||||
'((archive "tar" "rar" "zip" "xz")
|
||||
(image "jpg" "png" "jpeg" "webp")
|
||||
(audio "mp3" "flac" "ogg" "wav")
|
||||
(video "mkv" "webm" "mp4")
|
||||
(text "org" "txt" "md")
|
||||
(document "pdf" "epub" "info" "texi")
|
||||
(diff "diff" "patch")
|
||||
(program "sh" "el" "c" "py" "js" "html" "xml")
|
||||
(program-data "yaml" "yml" "toml" "conf" "json" "css"))
|
||||
"Some common file extensions grouped by type.")
|
||||
|
||||
(defun prot-common--get-file-type-regexp (type)
|
||||
"Return regular expression for file TYPE.
|
||||
TYPE is a `car' among the elements of `prot-common-file-extensions'."
|
||||
(when-let* ((extensions (alist-get type prot-common-file-extensions)))
|
||||
(format "\\.%s\\'" (regexp-opt extensions))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-common-number-even-p (n)
|
||||
"Test if N is an even number."
|
||||
|
|
@ -127,6 +142,45 @@ floating points: 16.666666666666664 => 16.667."
|
|||
(prot-common-seconds-to-hours seconds)
|
||||
(prot-common-seconds-to-minutes seconds)))
|
||||
|
||||
(defvar prot-common-temperature-history nil
|
||||
"Minibuffer history for temperature conversions.")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-common-fahrenheit-to-celsius (degree)
|
||||
"Convert Fahrenheit DEGREE to Celsius."
|
||||
(interactive
|
||||
(list
|
||||
(read-number "Fahrenheit DEGREE: " nil 'prot-common-temperature-history)))
|
||||
(let* ((converted-degrees (/ (- degree 32) 1.8))
|
||||
(f-string (number-to-string degree))
|
||||
(c-string (number-to-string converted-degrees)))
|
||||
(message "%s degrees Fahrenheit is %s degrees Celsius"
|
||||
(propertize f-string 'face 'error)
|
||||
(propertize c-string 'face 'success))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-common-celsius-to-fahrenheit (degree)
|
||||
"Convert Celsius DEGREE to Fahrenheit."
|
||||
(interactive
|
||||
(list
|
||||
(read-number "Celsius DEGREE: " nil 'prot-common-temperature-history)))
|
||||
(let* ((converted-degrees (+ (* degree 1.8) 32))
|
||||
(c-string (number-to-string degree))
|
||||
(f-string (number-to-string converted-degrees)))
|
||||
(message "%s degrees Celsius is %s degrees Fahrenheit"
|
||||
(propertize c-string 'face 'success)
|
||||
(propertize f-string 'face 'error))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-common--rotate (elements)
|
||||
"Rotate list ELEMENTS so that nth 0 is last, nth 1 is nth 0, and so on.
|
||||
Do it without side effects, unlike `prot-common-rotate-list-of-symbol'."
|
||||
(declare (side-effect-free t))
|
||||
(setq elements (copy-sequence elements))
|
||||
(let ((first (car elements))
|
||||
(rest (cdr elements)))
|
||||
(append rest (list first))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-common-rotate-list-of-symbol (symbol)
|
||||
"Rotate list value of SYMBOL by moving its car to the end.
|
||||
|
|
@ -167,7 +221,10 @@ will continue rotating accordingly."
|
|||
;;;###autoload
|
||||
(defun prot-common-truncate-lines-silently ()
|
||||
"Toggle line truncation without printing messages."
|
||||
(let ((inhibit-message t))
|
||||
;; NOTE 2026-08-28: I used to only do `inhibit-message' via a `let',
|
||||
;; but it was still printing messages.
|
||||
(cl-letf (((symbol-function 'message) #'always)
|
||||
(inhibit-message t))
|
||||
(toggle-truncate-lines t)))
|
||||
|
||||
;; NOTE 2023-08-12: I tried the `clear-message-function', but it did
|
||||
|
|
@ -227,7 +284,7 @@ If FRAME is non-nil, inspect the current frame."
|
|||
;;;###autoload
|
||||
(defun prot-common-completion-category ()
|
||||
"Return completion category."
|
||||
(when-let ((window (active-minibuffer-window)))
|
||||
(when-let* ((window (active-minibuffer-window)))
|
||||
(with-current-buffer (window-buffer window)
|
||||
(completion-metadata-get
|
||||
(completion-metadata (buffer-substring-no-properties
|
||||
|
|
@ -339,7 +396,7 @@ Return the exit code and output in a list."
|
|||
;;;###autoload
|
||||
(defun prot-common-auth-get-field (host prop)
|
||||
"Find PROP in `auth-sources' for HOST entry."
|
||||
(when-let ((source (auth-source-search :host host)))
|
||||
(when-let* ((source (auth-source-search :host host)))
|
||||
(if (eq prop :secret)
|
||||
(funcall (plist-get (car source) prop))
|
||||
(plist-get (flatten-list source) prop))))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-dired.el --- Extensions to dired.el for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2020-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2020-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my dired.el extensions, for use in my Emacs setup:
|
||||
;; https://protesilaos.com/emacs/dotemacs.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -44,6 +39,90 @@
|
|||
|
||||
;;;; General commands
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-dired-count-marks ()
|
||||
"Print message with the number of Dired marked files."
|
||||
(interactive nil dired-mode)
|
||||
(if-let* ((files (dired-get-marked-files))
|
||||
(length (length files))
|
||||
(_ (> length 1)))
|
||||
(message "%d files are marked" length)
|
||||
(user-error "No files found")))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-dired-open-directory-externally ()
|
||||
"Open the current directory in an external file manager."
|
||||
(interactive nil dired-mode)
|
||||
(if (derived-mode-p 'dired-mode)
|
||||
(call-process "xdg-open" nil 0 nil (expand-file-name default-directory))
|
||||
(user-error "Only do this in a Dired buffer")))
|
||||
|
||||
;;;; Flat Dired listing
|
||||
|
||||
(defvar prot-dired-regexp-history nil
|
||||
"Minibuffer history of `prot-dired-regexp-prompt'.")
|
||||
|
||||
(defun prot-dired-regexp-prompt ()
|
||||
(let ((default (car prot-dired-regexp-history)))
|
||||
(read-regexp
|
||||
(format-prompt "Files matching REGEXP" default)
|
||||
default 'prot-dired-regexp-history)))
|
||||
|
||||
(defun prot-dired--get-files (regexp)
|
||||
"Return files matching REGEXP, recursively from `default-directory'."
|
||||
(directory-files-recursively default-directory regexp nil))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-dired-search-flat-list (regexp)
|
||||
"Return a Dired buffer for files matching REGEXP.
|
||||
Perform the search recursively from the current directory."
|
||||
(interactive (list (prot-dired-regexp-prompt)))
|
||||
(if-let* ((files (prot-dired--get-files regexp))
|
||||
(relative-paths (mapcar #'file-relative-name files)))
|
||||
(dired (cons (format "prot-flat-dired for `%s'" regexp) relative-paths))
|
||||
(error "No files matching `%s'" regexp)))
|
||||
|
||||
(defvar prot-dired-days-prompt-history nil
|
||||
"Minibuffer history for `prot-dired-days-prompt'.")
|
||||
|
||||
(defun prot-dired-days-prompt ()
|
||||
"Prompt for days and return them as a number."
|
||||
(let* ((first (car prot-dired-days-prompt-history))
|
||||
(default (when (stringp first)
|
||||
(string-to-number first))))
|
||||
(read-number "Number of days: " default 'prot-dired-days-prompt-history)))
|
||||
|
||||
(defun prot-dired--get-last-modified (files days)
|
||||
"Return list of FILES last modified since DAYS."
|
||||
(seq-filter
|
||||
(lambda (file)
|
||||
(and-let* ((attributes (file-attributes file))
|
||||
(last-modified (nth 5 attributes))
|
||||
(last-modified-seconds (time-to-seconds last-modified))
|
||||
(current-time (current-time))
|
||||
(current-time-seconds (time-to-seconds current-time))
|
||||
(delta-seconds (* days 24 60 60))
|
||||
(oldest-seconds (- current-time-seconds delta-seconds))
|
||||
(_ (>= last-modified-seconds oldest-seconds)))))
|
||||
files))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-dired-search-flat-list-since-days (regexp days)
|
||||
"Return Dired buffer with files matching REGEXP up to DAYS since last modification.
|
||||
Perform the search recursively from the current directory."
|
||||
(interactive
|
||||
(list
|
||||
(prot-dired-regexp-prompt)
|
||||
(prot-dired-days-prompt)))
|
||||
(if-let* ((files (prot-dired--get-files regexp)))
|
||||
(if-let* ((files-filtered (prot-dired--get-last-modified files days))
|
||||
(relative-paths (mapcar #'file-relative-name files-filtered)))
|
||||
(dired (cons (format "prot-flat-dired since %d days for `%s'" days regexp) relative-paths))
|
||||
(error "No files last modified within the last %d days" days))
|
||||
(error "No files matching `%s'" regexp)))
|
||||
|
||||
;;;; General commands
|
||||
|
||||
;; NOTE 2023-06-27: This user option is quick-and-dirty. I prefer not
|
||||
;; to have an option at all and simply do the right thing based on
|
||||
;; `dired-guess-shell-alist-user'.
|
||||
|
|
@ -92,51 +171,36 @@ Restore the buffer with \\<dired-mode-map>`\\[revert-buffer]'."
|
|||
(dired-do-kill-lines)
|
||||
(add-to-history 'prot-dired--limit-hist regexp))
|
||||
|
||||
(defvar prot-dired--find-grep-hist '()
|
||||
"Minibuffer history for `prot-dired-grep-marked-files'.")
|
||||
(defvar prot-dired-grep-marked-files-history nil
|
||||
"Minibuffer history for `prot-dired-grep-marked-files-prompt'.")
|
||||
|
||||
(defun prot-dired-grep-marked-files-prompt ()
|
||||
"Prompt for string to search for with `prot-dired-grep-marked-files'."
|
||||
(read-string
|
||||
"grep for PATTERN in marked files: "
|
||||
nil 'prot-dired-grep-marked-files-history))
|
||||
|
||||
;; Also see `prot-search-grep' from prot-search.el.
|
||||
;;;###autoload
|
||||
(defun prot-dired-grep-marked-files (regexp &optional arg)
|
||||
"Run `find' with `grep' for REGEXP on marked files.
|
||||
When no files are marked or when just a single one is marked,
|
||||
search the entire directory instead.
|
||||
|
||||
With optional prefix ARG target a single marked file.
|
||||
|
||||
We assume that there is no point in marking a single file and
|
||||
running find+grep on its contents. Visit it and call `occur' or
|
||||
run grep directly on it without the whole find part."
|
||||
(defun prot-dired-grep-marked-files (files regexp)
|
||||
"Run `find' with `grep' for REGEXP on marked Dired FILES.
|
||||
If FILES contains directories, search through them recursively.
|
||||
Ignore .git directories."
|
||||
(interactive
|
||||
(list
|
||||
(read-string "grep for PATTERN (marked files OR current directory): " nil 'prot-dired--find-grep-hist)
|
||||
current-prefix-arg)
|
||||
dired-mode)
|
||||
(when-let* ((marks (dired-get-marked-files 'no-dir))
|
||||
(files (mapconcat #'identity marks " "))
|
||||
(args (if (or arg (length> marks 1))
|
||||
;; Thanks to Sean Whitton for pointing out an
|
||||
;; earlier superfluity of mine: we do not need
|
||||
;; to call grep through find when we already
|
||||
;; know the files we want to search in. Check
|
||||
;; Sean's dotfiles:
|
||||
;; <https://git.spwhitton.name/dotfiles>.
|
||||
;;
|
||||
;; Any other errors or omissions are my own.
|
||||
(format "grep -nH --color=auto %s %s" (shell-quote-argument regexp) files)
|
||||
(concat
|
||||
"find . -not " (shell-quote-argument "(")
|
||||
" -wholename " (shell-quote-argument "*/.git*")
|
||||
" -prune " (shell-quote-argument ")")
|
||||
" -type f"
|
||||
" -exec grep -nHE --color=auto " regexp " "
|
||||
(shell-quote-argument "{}")
|
||||
" " (shell-quote-argument ";") " "))))
|
||||
(compilation-start
|
||||
args
|
||||
'grep-mode
|
||||
(lambda (mode) (format "*prot-dired-find-%s for '%s'" mode regexp))
|
||||
t)))
|
||||
(if-let* ((marks (dired-get-marked-files)))
|
||||
(list marks (prot-dired-grep-marked-files-prompt))
|
||||
(user-error "Mark one or more files or directories")))
|
||||
(compilation-start
|
||||
(concat
|
||||
"find " (mapconcat #'shell-quote-argument files " ")
|
||||
" -path " (shell-quote-argument "*/.git") " -prune "
|
||||
" -o -type f "
|
||||
" -exec grep -nHER --color=auto " (shell-quote-argument regexp) " "
|
||||
(shell-quote-argument "{}")
|
||||
" " (shell-quote-argument "+"))
|
||||
'grep-mode
|
||||
(lambda (_mode) (format "*prot-dired-grep-marked for `%s'*" regexp))
|
||||
t))
|
||||
|
||||
;;;; Subdir extras and Imenu setup
|
||||
|
||||
|
|
@ -180,7 +244,7 @@ For more on such headings, read `dired-maybe-insert-subdir'."
|
|||
(while (and (prot-dired-subdirectory-previous)
|
||||
(not (equal (dired-current-directory)
|
||||
(expand-file-name default-directory))))
|
||||
(dired-kill-subdir)))
|
||||
(dired-kill-subdir)))
|
||||
|
||||
(autoload 'cl-remove-if-not "cl-seq")
|
||||
|
||||
|
|
@ -208,10 +272,10 @@ For more on such headings, read `dired-maybe-insert-subdir'."
|
|||
When items are marked, insert those which are subsirectories of
|
||||
the current directory. Ignore regular files.
|
||||
|
||||
If no marks are active and point is on a subdirectory line,
|
||||
If no files are active and point is on a subdirectory line,
|
||||
insert it directly.
|
||||
|
||||
If no marks are active and point is not on a subdirectory line,
|
||||
If no files are active and point is not on a subdirectory line,
|
||||
prompt for a subdirectory using completion.
|
||||
|
||||
With optional ARG as a single prefix (`\\[universal-argument]')
|
||||
|
|
|
|||
76
emacs/.emacs.d/prot-lisp/prot-ediff.el
Normal file
76
emacs/.emacs.d/prot-lisp/prot-ediff.el
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
;;; prot-ediff.el --- Ediff extensions for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2025-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Extensions for Ediff, intended for use in my Emacs setup:
|
||||
;; https://protesilaos.com/emacs/dotemacs.
|
||||
|
||||
;;; Code:
|
||||
(require 'ediff)
|
||||
|
||||
(defun prot-ediff-store-layout ()
|
||||
"Store current frame window configuration as a frame parameter.
|
||||
Add this function to the `ediff-before-setup-hook'.
|
||||
|
||||
Also see `prot-ediff-restore-layout'."
|
||||
(let ((frame (selected-frame)))
|
||||
(set-frame-parameter
|
||||
frame
|
||||
'prot-ediff-last-layout
|
||||
(current-window-configuration frame))))
|
||||
|
||||
(defun prot-ediff-restore-layout ()
|
||||
"Restore the frame's window configuration.
|
||||
Add this function to the `ediff-quit-hook'.
|
||||
|
||||
Also see `prot-ediff-store-layout'."
|
||||
(if-let* ((layout (frame-parameter (selected-frame) 'prot-ediff-last-layout)))
|
||||
(set-window-configuration layout)
|
||||
;; We do not signal a `user-error' here because that would prevent
|
||||
;; `ediff-quit' from closing the Ediff session.
|
||||
(message "No Ediff window configuration for the current frame")))
|
||||
|
||||
(defun prot-ediff-visible-buffers-2 ()
|
||||
"Run ediff on the buffers displayed in the current frame's two windows."
|
||||
(interactive)
|
||||
(if-let* ((windows (window-list))
|
||||
(_ (= (length windows) 2))
|
||||
(buffers (mapcar #'window-buffer windows)))
|
||||
(pcase-let ((`(,first ,second) buffers))
|
||||
(ediff-buffers first second))
|
||||
(user-error "Can only operate on two windows")))
|
||||
|
||||
(defun prot-ediff-visible-buffers-3 ()
|
||||
"Run ediff on the buffers displayed in the current frame's three windows."
|
||||
(interactive)
|
||||
(if-let* ((windows (window-list))
|
||||
(_ (= (length windows) 3))
|
||||
(buffers (mapcar #'window-buffer windows)))
|
||||
(pcase-let ((`(,first ,second ,third) buffers))
|
||||
(ediff-buffers3 first second third))
|
||||
(user-error "Can only operate on three windows")))
|
||||
|
||||
(provide 'prot-ediff)
|
||||
;;; prot-ediff.el ends here
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
;;; prot-elfeed.el --- Elfeed extensions for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 Protesilaos Stavrou
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; NOTE 2022-06-08: This is old code. There are things I would like to
|
||||
;; improve.
|
||||
|
||||
;;
|
||||
;; Extensions for Elfeed, intended for use in my Emacs setup:
|
||||
;; https://protesilaos.com/emacs/dotemacs.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'subr-x))
|
||||
(require 'elfeed nil t)
|
||||
(require 'url-util)
|
||||
(require 'prot-common)
|
||||
|
||||
(defgroup prot-elfeed ()
|
||||
"Personal extensions for Elfeed."
|
||||
:group 'elfeed)
|
||||
|
||||
(defcustom prot-elfeed-feeds-file
|
||||
(thread-last user-emacs-directory (expand-file-name "feeds.el.gpg"))
|
||||
"Path to file with `elfeed-feeds'."
|
||||
:type 'string
|
||||
:group 'prot-elfeed)
|
||||
|
||||
(defcustom prot-elfeed-archives-directory "~/Documents/feeds/"
|
||||
"Path to directory for storing Elfeed entries."
|
||||
:type 'string
|
||||
:group 'prot-elfeed)
|
||||
|
||||
(defcustom prot-elfeed-tag-faces nil
|
||||
"Add faces for certain tags.
|
||||
The tags are: critical, important, personal."
|
||||
:type 'boolean
|
||||
:group 'prot-elfeed)
|
||||
|
||||
(defcustom prot-elfeed-search-tags '(critical important personal)
|
||||
"List of user-defined tags.
|
||||
Used by `prot-elfeed-toggle-tag'."
|
||||
:type 'list
|
||||
:group 'prot-elfeed)
|
||||
|
||||
(defface prot-elfeed-entry-critical '((t :inherit font-lock-warning-face))
|
||||
"Face for Elfeed entries tagged with `critical'.")
|
||||
|
||||
(defface prot-elfeed-entry-important '((t :inherit font-lock-constant-face))
|
||||
"Face for Elfeed entries tagged with `important'.")
|
||||
|
||||
(defface prot-elfeed-entry-personal '((t :inherit font-lock-variable-name-face))
|
||||
"Face for Elfeed entries tagged with `personal'.")
|
||||
|
||||
;;;; Utilities
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-elfeed-load-feeds ()
|
||||
"Load file containing the `elfeed-feeds' list.
|
||||
Add this to `elfeed-search-mode-hook'."
|
||||
(let ((feeds prot-elfeed-feeds-file))
|
||||
(if (file-exists-p feeds)
|
||||
(load-file feeds)
|
||||
(user-error "Missing feeds' file"))))
|
||||
|
||||
(defvar elfeed-search-face-alist)
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-elfeed-fontify-tags ()
|
||||
"Expand Elfeed faces if `prot-elfeed-tag-faces' is non-nil."
|
||||
(if prot-elfeed-tag-faces
|
||||
(setq elfeed-search-face-alist
|
||||
'((critical prot-elfeed-entry-critical)
|
||||
(important prot-elfeed-entry-important)
|
||||
(personal prot-elfeed-entry-personal)
|
||||
(unread elfeed-search-unread-title-face)))
|
||||
(setq elfeed-search-face-alist
|
||||
'((unread elfeed-search-unread-title-face)))))
|
||||
|
||||
(defvar prot-elfeed--tag-hist '()
|
||||
"History of inputs for `prot-elfeed-toggle-tag'.")
|
||||
|
||||
(defun prot-elfeed--character-prompt (tags)
|
||||
"Helper of `prot-elfeed-toggle-tag' to read TAGS."
|
||||
(let ((def (car prot-elfeed--tag-hist)))
|
||||
(completing-read
|
||||
(format "Toggle tag [%s]: " def)
|
||||
tags nil t nil 'prot-elfeed--tag-hist def)))
|
||||
|
||||
(defvar elfeed-show-entry)
|
||||
(declare-function elfeed-tagged-p "elfeed")
|
||||
(declare-function elfeed-search-toggle-all "elfeed")
|
||||
(declare-function elfeed-show-tag "elfeed")
|
||||
(declare-function elfeed-show-untag "elfeed")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-elfeed-toggle-tag (tag)
|
||||
"Toggle TAG for the current item.
|
||||
|
||||
When the region is active in the `elfeed-search-mode' buffer, all
|
||||
entries encompassed by it are affected. Otherwise the item at
|
||||
point is the target. For `elfeed-show-mode', the current entry
|
||||
is always the target.
|
||||
|
||||
The list of tags is provided by `prot-elfeed-search-tags'."
|
||||
(interactive
|
||||
(list
|
||||
(intern
|
||||
(prot-elfeed--character-prompt prot-elfeed-search-tags))))
|
||||
(if (derived-mode-p 'elfeed-show-mode)
|
||||
(if (elfeed-tagged-p tag elfeed-show-entry)
|
||||
(elfeed-show-untag tag)
|
||||
(elfeed-show-tag tag))
|
||||
(elfeed-search-toggle-all tag)))
|
||||
|
||||
(defvar elfeed-show-truncate-long-urls)
|
||||
(declare-function elfeed-entry-title "elfeed")
|
||||
(declare-function elfeed-show-refresh "elfeed")
|
||||
|
||||
;;;; General commands
|
||||
|
||||
(defvar elfeed-search-filter-active)
|
||||
(defvar elfeed-search-filter)
|
||||
(declare-function elfeed-db-get-all-tags "elfeed")
|
||||
(declare-function elfeed-search-update "elfeed")
|
||||
(declare-function elfeed-search-clear-filter "elfeed")
|
||||
|
||||
(defun prot-elfeed--format-tags (tags sign)
|
||||
"Prefix SIGN to each tag in TAGS."
|
||||
(mapcar (lambda (tag)
|
||||
(format "%s%s" sign tag))
|
||||
tags))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-elfeed-search-tag-filter ()
|
||||
"Filter Elfeed search buffer by tags using completion.
|
||||
|
||||
Completion accepts multiple inputs, delimited by `crm-separator'.
|
||||
Arbitrary input is also possible, but you may have to exit the
|
||||
minibuffer with something like `exit-minibuffer'."
|
||||
(interactive)
|
||||
(unwind-protect
|
||||
(elfeed-search-clear-filter)
|
||||
(let* ((elfeed-search-filter-active :live)
|
||||
(db-tags (elfeed-db-get-all-tags))
|
||||
(plus-tags (prot-elfeed--format-tags db-tags "+"))
|
||||
(minus-tags (prot-elfeed--format-tags db-tags "-"))
|
||||
(all-tags (delete-dups (append plus-tags minus-tags)))
|
||||
(tags (completing-read-multiple
|
||||
"Apply one or more tags: "
|
||||
all-tags #'prot-common-crm-exclude-selected-p t))
|
||||
(input (string-join `(,elfeed-search-filter ,@tags) " ")))
|
||||
(setq elfeed-search-filter input))
|
||||
(elfeed-search-update :force)))
|
||||
|
||||
(provide 'prot-elfeed)
|
||||
;;; prot-elfeed.el ends here
|
||||
94
emacs/.emacs.d/prot-lisp/prot-elisp.el
Normal file
94
emacs/.emacs.d/prot-lisp/prot-elisp.el
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
;;; prot-elisp.el --- Emacs Lisp extras for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2025-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Extensions for Emacs Lisp, intended for use in my Emacs setup:
|
||||
;; https://protesilaos.com/emacs/dotemacs.
|
||||
|
||||
;;; Code:
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-elisp-eval-and-print-last-sexp ()
|
||||
"Evaluate and print expression before point like `eval-print-last-sexp'.
|
||||
Prepend a comment to the return value. Also copy the return value to
|
||||
the `kill-ring' and set the mark to where point was before inserting the
|
||||
return value."
|
||||
(declare (interactive-only t))
|
||||
(interactive)
|
||||
(if-let* ((string (thing-at-point 'sexp :no-properties))
|
||||
(_ (not (string-prefix-p ";" string)))
|
||||
(expression (read string)))
|
||||
(let ((return-value (eval expression)))
|
||||
(kill-new (format "%S" return-value))
|
||||
(message "Copied: `%S'" return-value)
|
||||
(push-mark (point))
|
||||
(insert (format "\n%S\n" return-value))
|
||||
(string-insert-rectangle (+ (mark) 1) (- (point) 1) ";; => "))
|
||||
(user-error "No expression at point")))
|
||||
|
||||
(defvar-keymap prot-elisp-macroexpand-mode-map
|
||||
:doc "keymap for `prot-elisp-macroexpand-mode'."
|
||||
:parent special-mode-map)
|
||||
|
||||
(define-derived-mode prot-elisp-macroexpand-mode emacs-lisp-mode "MacroExpand"
|
||||
"Like `emacs-lisp-mode' but for macroexpanded forms."
|
||||
:interactive nil
|
||||
(read-only-mode 1)
|
||||
(display-line-numbers-mode 1))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-elisp-pp-macroexpand-last-sexp ()
|
||||
"Like `pp-macroexpand-last-sexp' but with a generic `display-buffer'.
|
||||
Now use `display-buffer-alist' like the Lisp gods intended."
|
||||
(declare (interactive-only t))
|
||||
(interactive)
|
||||
(if-let* ((thing (thing-at-point 'sexp :no-properties))
|
||||
(expression (read thing))
|
||||
(buffer (get-buffer-create "*prot-elisp-macroexpand*"))
|
||||
(inhibit-read-only t))
|
||||
(progn
|
||||
(with-current-buffer buffer
|
||||
(erase-buffer)
|
||||
(insert (format "%S" (macroexpand-1 expression)))
|
||||
(prot-elisp-macroexpand-mode)
|
||||
(pp-buffer))
|
||||
(display-buffer buffer))
|
||||
(user-error "No expression to macroexpand")))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-elisp-copy-current-definition ()
|
||||
"Copy the symbol of the definition around point."
|
||||
(interactive)
|
||||
(if-let* ((text (save-excursion
|
||||
(beginning-of-defun)
|
||||
(buffer-substring-no-properties (line-beginning-position) (line-end-position))))
|
||||
(definition (replace-regexp-in-string "(.+?\s+\\(.+?\\)\s+.*" "\\1" text)))
|
||||
(progn
|
||||
(kill-new definition)
|
||||
(message "Copied `%s'" definition))
|
||||
(message "Cannot find definition near point")))
|
||||
|
||||
(provide 'prot-elisp)
|
||||
;;; prot-elisp.el ends here
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-embark.el --- Custom Embark keymaps -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1") (embark "0.23"))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
;;; prot-eww.el --- Extensions for EWW -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 Protesilaos Stavrou, Abhiseck Paira
|
||||
;; Copyright (C) 2021-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Abhiseck Paira <abhiseckpaira@disroot.org>
|
||||
;; Maintainer: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -28,359 +27,79 @@
|
|||
;;
|
||||
;; Extensions for the eww, intended for my Emacs setup:
|
||||
;; <https://protesilaos.com/emacs/dotemacs/>.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;; XXX NOTE XXX 2023-05-19: Much of this code is severely out of date.
|
||||
;; I plan to review it. DO NOT USE!!!
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'shr)
|
||||
(require 'eww)
|
||||
(require 'url-parse)
|
||||
(require 'prot-common)
|
||||
|
||||
(defgroup prot-eww ()
|
||||
(defgroup prot-eww nil
|
||||
"Tweaks for EWW."
|
||||
:group 'eww)
|
||||
|
||||
;;;; Basic setup
|
||||
|
||||
;; TODO 2021-10-15: Deprecate this in favour of what we added to Emacs29.
|
||||
;; <https://protesilaos.com/codelog/2021-10-15-emacs-29-eww-rename-buffers/>.
|
||||
|
||||
(defun prot-eww--rename-buffer ()
|
||||
"Rename EWW buffer using page title or URL.
|
||||
To be used by `eww-after-render-hook'."
|
||||
(let ((name (if (equal "" (plist-get eww-data :title))
|
||||
(plist-get eww-data :url)
|
||||
(plist-get eww-data :title))))
|
||||
(rename-buffer (format "*%s # eww*" name) t)))
|
||||
|
||||
(add-hook 'eww-after-render-hook #'prot-eww--rename-buffer)
|
||||
(advice-add 'eww-back-url :after #'prot-eww--rename-buffer)
|
||||
(advice-add 'eww-forward-url :after #'prot-eww--rename-buffer)
|
||||
|
||||
;;;; History extras
|
||||
|
||||
(defvar prot-eww-visited-history '()
|
||||
"History of visited URLs.")
|
||||
|
||||
(defcustom prot-eww-save-history-file
|
||||
(locate-user-emacs-file "prot-eww-visited-history")
|
||||
"File to save the value of `prot-eww-visited-history'."
|
||||
:type 'file
|
||||
:group 'prot-eww)
|
||||
|
||||
(defcustom prot-eww-save-visited-history nil
|
||||
"Whether to save `prot-eww-visited-history'.
|
||||
If non-nil, save the value of `prot-eww-visited-history' in
|
||||
`prot-eww-save-history-file'."
|
||||
:type 'boolean
|
||||
:group 'prot-eww)
|
||||
|
||||
(defcustom prot-eww-list-history-buffer "*prot-eww-history*"
|
||||
"Name of buffer for `prot-eww-list-history'."
|
||||
:type 'string
|
||||
:group 'prot-eww)
|
||||
|
||||
;; These history related functions are adapted from eww.
|
||||
(defun prot-eww--save-visited-history ()
|
||||
"Save the value of `prot-eww-visited-history' in a file.
|
||||
The file is determined by the variable `prot-eww-save-history-file'."
|
||||
(when prot-eww-save-visited-history
|
||||
(with-temp-file prot-eww-save-history-file
|
||||
(insert (concat ";; Auto-generated file;"
|
||||
" don't edit -*- mode: lisp-data -*-\n"))
|
||||
(pp prot-eww-visited-history (current-buffer)))))
|
||||
|
||||
(defun prot-eww--read-visited-history (&optional error-out)
|
||||
"Read history from `prot-eww-save-history-file'.
|
||||
If ERROR-OUT, signal `user-error' if there is no history."
|
||||
(when prot-eww-save-visited-history
|
||||
(let ((file prot-eww-save-history-file))
|
||||
(setq prot-eww-visited-history
|
||||
(unless (zerop
|
||||
(or (file-attribute-size (file-attributes file))
|
||||
0))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(read (current-buffer)))))
|
||||
(when (and error-out (not prot-eww-visited-history))
|
||||
(user-error "No history is defined")))))
|
||||
|
||||
(unless prot-eww-visited-history
|
||||
(prot-eww--read-visited-history t))
|
||||
|
||||
(defun prot-eww--history-prepare ()
|
||||
"Prepare dedicated buffer for browsing history."
|
||||
(set-buffer (get-buffer-create prot-eww-list-history-buffer))
|
||||
(prot-eww-history-mode)
|
||||
(let ((inhibit-read-only t)
|
||||
start)
|
||||
(erase-buffer)
|
||||
(setq-local header-line-format
|
||||
"EWW Browsing History (prot-eww)")
|
||||
(dolist (history prot-eww-visited-history)
|
||||
(setq start (point))
|
||||
(insert (format "%s" history) "\n")
|
||||
(put-text-property start (1+ start) 'prot-eww-history history))
|
||||
(goto-char (point-min))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-list-history ()
|
||||
"Display `prot-eww-visited-history' in a dedicated buffer.
|
||||
This is a replacement for `eww-list-histories' (or equivalent),
|
||||
as it can combine URLs in the Gopher or Gemini protocols."
|
||||
(interactive)
|
||||
(when prot-eww-visited-history
|
||||
(prot-eww--save-visited-history))
|
||||
(prot-eww--read-visited-history t)
|
||||
(pop-to-buffer prot-eww-list-history-buffer)
|
||||
(prot-eww--history-prepare))
|
||||
|
||||
(defvar prot-eww-history-kill-ring nil
|
||||
"Store the killed history element.")
|
||||
|
||||
(defun prot-eww-history-kill ()
|
||||
"Kill the current history."
|
||||
(interactive)
|
||||
(let* ((start (line-beginning-position))
|
||||
(history (get-text-property start 'prot-eww-history))
|
||||
(inhibit-read-only t))
|
||||
(unless history
|
||||
(user-error "No history on the current line"))
|
||||
(forward-line 1)
|
||||
(push (buffer-substring start (point))
|
||||
prot-eww-history-kill-ring)
|
||||
(delete-region start (point))
|
||||
(setq prot-eww-visited-history (delq history
|
||||
prot-eww-visited-history))
|
||||
(prot-eww--save-visited-history)))
|
||||
|
||||
(defun prot-eww-history-yank ()
|
||||
"Yank a previously killed history to the current line."
|
||||
(interactive)
|
||||
(unless prot-eww-history-kill-ring
|
||||
(user-error "No previously killed history"))
|
||||
(beginning-of-line)
|
||||
(let ((inhibit-read-only t)
|
||||
(start (point))
|
||||
history)
|
||||
(insert (pop prot-eww-history-kill-ring))
|
||||
(setq history (get-text-property start 'prot-eww-history))
|
||||
(if (= start (point-min))
|
||||
(push history prot-eww-visited-history)
|
||||
(let ((line (count-lines start (point))))
|
||||
(setcdr (nthcdr (1- line) prot-eww-visited-history)
|
||||
(cons history (nthcdr line
|
||||
prot-eww-visited-history)))))
|
||||
(prot-eww--save-visited-history)))
|
||||
|
||||
(defun prot-eww-history-browse ()
|
||||
"Browse the history under point."
|
||||
(interactive)
|
||||
(let ((history (get-text-property (line-beginning-position)
|
||||
'prot-eww-history)))
|
||||
(unless history
|
||||
(user-error "No history on the current line"))
|
||||
(quit-window)
|
||||
(prot-eww history)))
|
||||
|
||||
(defvar prot-eww-history-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "C-k") 'prot-eww-history-kill)
|
||||
(define-key map (kbd "C-y") 'prot-eww-history-yank)
|
||||
(define-key map (kbd "<RET>") 'prot-eww-history-browse)
|
||||
|
||||
(easy-menu-define nil map
|
||||
"Menu for `prot-eww-history-mode-map'."
|
||||
'("prot-eww history"
|
||||
["Exit" quit-window t]
|
||||
["Browse" prot-eww-history-browse
|
||||
:active (get-text-property (line-beginning-position)
|
||||
'prot-eww-history)]
|
||||
["Kill" prot-eww-history-kill
|
||||
:active (get-text-property (line-beginning-position)
|
||||
'prot-eww-history)]
|
||||
["Yank" prot-eww-history-yank
|
||||
:active prot-eww-history-kill-ring]))
|
||||
map))
|
||||
|
||||
(define-derived-mode prot-eww-history-mode
|
||||
special-mode
|
||||
"prot-eww-history"
|
||||
"Mode for listing history.
|
||||
|
||||
\\{prot-eww-history-mode-map}"
|
||||
(buffer-disable-undo)
|
||||
(setq truncate-lines t))
|
||||
|
||||
(defun prot-eww--record-history ()
|
||||
"Store URL in `prot-eww-visited-history'.
|
||||
To be used by `eww-after-render-hook'."
|
||||
(let ((url (plist-get eww-data :url)))
|
||||
(add-to-history 'prot-eww-visited-history url)))
|
||||
|
||||
(add-hook 'eww-after-render-hook #'prot-eww--record-history)
|
||||
(advice-add 'eww-back-url :after #'prot-eww--record-history)
|
||||
(advice-add 'eww-forward-url :after #'prot-eww--record-history)
|
||||
;; Is there a better function to add this advice?
|
||||
|
||||
;;;; Commands
|
||||
|
||||
;; handler that browse-url calls.
|
||||
|
||||
(defun prot-eww--get-current-url ()
|
||||
"Return the current-page's URL."
|
||||
(when (eq major-mode 'eww-mode)
|
||||
(plist-get eww-data :url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww (url &optional arg)
|
||||
"Pass URL to appropriate client.
|
||||
With optional ARG, use a new buffer."
|
||||
(interactive (list (browse-url-interactive-arg "URL: ") current-prefix-arg))
|
||||
(eww url arg))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-browse-dwim (url &optional arg)
|
||||
"Visit a URL, maybe from `eww-prompt-history', with completion.
|
||||
|
||||
With optional prefix ARG (\\[universal-argument]) open URL in a
|
||||
new eww buffer. If URL does not look like a valid link, run a
|
||||
web query using `eww-search-prefix'.
|
||||
|
||||
When called from an eww buffer, provide the current link as
|
||||
\\<minibuffer-local-map>\\[next-history-element]."
|
||||
(interactive
|
||||
(let ((all-history (delete-dups
|
||||
(append prot-eww-visited-history
|
||||
eww-prompt-history)))
|
||||
(current-url (prot-eww--get-current-url)))
|
||||
(list
|
||||
(completing-read "Run EWW on: " all-history
|
||||
nil nil current-url 'eww-prompt-history current-url)
|
||||
(prefix-numeric-value current-prefix-arg))))
|
||||
(prot-eww url arg))
|
||||
|
||||
;; NOTE 2021-09-08: This uses the EWW-specific bookmarks, NOT those of
|
||||
;; bookmark.el. Further below I provide integration with the latter,
|
||||
;; meaning that we must either make this obsolete or make it work with
|
||||
;; the new system.
|
||||
;;;###autoload
|
||||
(defun prot-eww-visit-bookmark (&optional arg)
|
||||
"Visit bookmarked URL.
|
||||
|
||||
With optional prefix ARG (\\[universal-argument]) open URL in a
|
||||
new EWW buffer."
|
||||
(interactive "P")
|
||||
(eww-read-bookmarks)
|
||||
(let ((list (gensym)))
|
||||
(dolist (bookmark eww-bookmarks)
|
||||
(push (plist-get bookmark :url) list))
|
||||
(if eww-bookmarks
|
||||
(eww (completing-read "Visit EWW bookmark: " list)
|
||||
(when arg 4))
|
||||
(user-error "No bookmarks"))))
|
||||
|
||||
(defun prot-eww--capture-url-on-page (&optional position)
|
||||
"Capture all the links on the current web page.
|
||||
|
||||
Return a list of strings. Strings are in the form LABEL @ URL.
|
||||
When optional argument POSITION is non-nil, include position info
|
||||
in the strings too, so strings take the form
|
||||
LABEL @ URL ~ POSITION."
|
||||
(let (links match)
|
||||
(defun prot-eww--get-urls ()
|
||||
"Get all links in the current buffer."
|
||||
(let ((links nil))
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
;; NOTE 2021-07-25: The first clause in the `or' is meant to
|
||||
;; address a bug where if a URL is in `point-min' it does not get
|
||||
;; captured.
|
||||
(while (setq match (text-property-search-backward 'shr-url))
|
||||
(let* ((raw-url (prop-match-value match))
|
||||
(start-point-prop (prop-match-beginning match))
|
||||
(end-point-prop (prop-match-end match))
|
||||
(url (when (stringp raw-url)
|
||||
(propertize raw-url 'face 'link)))
|
||||
(label (replace-regexp-in-string "\n" " " ; NOTE 2021-07-25: newlines break completion
|
||||
(buffer-substring-no-properties
|
||||
start-point-prop end-point-prop)))
|
||||
(point start-point-prop)
|
||||
(line (line-number-at-pos point t))
|
||||
(column (save-excursion (goto-char point) (current-column)))
|
||||
(coordinates (propertize
|
||||
(format "%d,%d (%d)" line column point)
|
||||
'face 'shadow)))
|
||||
(when url
|
||||
(if position
|
||||
(push (format "%-15s ~ %s @ %s"
|
||||
coordinates label url)
|
||||
links)
|
||||
(push (format "%s @ %s"
|
||||
label url)
|
||||
links))))))
|
||||
links))
|
||||
(goto-char (point-min))
|
||||
(while (text-property-search-forward 'face 'shr-link)
|
||||
(when-let* ((position (point))
|
||||
(button (button-at position)))
|
||||
(push
|
||||
(list position
|
||||
(button-label button)
|
||||
(shr-url-at-point nil))
|
||||
links))))
|
||||
(nreverse links)))
|
||||
|
||||
(defmacro prot-eww-act-visible-window (&rest body)
|
||||
"Run BODY within narrowed-region.
|
||||
If region is active run BODY within active region instead.
|
||||
Return the value of the last form of BODY."
|
||||
`(save-restriction
|
||||
(if (use-region-p)
|
||||
(narrow-to-region (region-beginning) (region-end))
|
||||
(narrow-to-region (window-start) (window-end)))
|
||||
,@body))
|
||||
(defun prot-eww-buffer-url-prompt ()
|
||||
"Prompt for a url in the current buffer."
|
||||
(when-let* ((link-data (prot-eww--get-urls))
|
||||
(candidates (mapcar
|
||||
(pcase-lambda (`(,position ,name ,_))
|
||||
(format "%s %s" position name))
|
||||
link-data))
|
||||
(table (prot-common-completion-table-no-sort nil candidates))
|
||||
(selection
|
||||
(completing-read
|
||||
(format-prompt "Select link in the current page" nil)
|
||||
table))
|
||||
(position (car (split-string selection "\t")))
|
||||
(number (string-to-number position)))
|
||||
(assoc number link-data)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-visit-url-on-page (&optional arg)
|
||||
"Visit URL from list of links on the page using completion.
|
||||
(defun prot-eww-visit-url-on-page (&optional new-buffer)
|
||||
"Visit URL among those in the current buffer using completion.
|
||||
With optional NEW-BUFFER as a prefix argument, visit the URL in a new
|
||||
buffer instead of the current one."
|
||||
(interactive "P" eww-mode)
|
||||
(unless (derived-mode-p 'eww-mode)
|
||||
(user-error "This command only works in an EWW buffer"))
|
||||
(if-let* ((data (prot-eww-buffer-url-prompt))
|
||||
(url (nth 2 data)))
|
||||
(eww url new-buffer)
|
||||
(error "Cannot find URL in data `%s'" data)))
|
||||
|
||||
With optional prefix ARG (\\[universal-argument]) open URL in a
|
||||
new EWW buffer."
|
||||
(interactive "P")
|
||||
(when (derived-mode-p 'eww-mode)
|
||||
(let* ((links (prot-eww--capture-url-on-page))
|
||||
(selection (completing-read "Browse URL from page: " links nil t))
|
||||
(url (replace-regexp-in-string ".*@ " "" selection)))
|
||||
(eww url (when arg 4)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-jump-to-url-on-page (&optional arg)
|
||||
"Jump to URL position on the page using completion.
|
||||
|
||||
When called without ARG (\\[universal-argument]) get URLs only
|
||||
from the visible portion of the buffer. But when ARG is provided
|
||||
consider whole buffer."
|
||||
(interactive "P")
|
||||
(when (derived-mode-p 'eww-mode)
|
||||
(let* ((links
|
||||
(if arg
|
||||
(prot-eww--capture-url-on-page t)
|
||||
(prot-eww-act-visible-window
|
||||
(prot-eww--capture-url-on-page t))))
|
||||
(prompt-scope (if arg
|
||||
(propertize "URL on the page" 'face 'warning)
|
||||
"visible URL"))
|
||||
(prompt (format "Jump to %s: " prompt-scope))
|
||||
(selection (completing-read prompt links nil t))
|
||||
(position (replace-regexp-in-string "^.*(\\([0-9]+\\))[\s\t]+~" "\\1" selection))
|
||||
(point (string-to-number position)))
|
||||
(goto-char point))))
|
||||
(defun prot-eww-jump-to-url-on-page ()
|
||||
"Go the position of a URL among those in the current buffer."
|
||||
(interactive nil eww-mode)
|
||||
(unless (derived-mode-p 'eww-mode)
|
||||
(user-error "This command only works in an EWW buffer"))
|
||||
(if-let* ((data (prot-eww-buffer-url-prompt))
|
||||
(position (car data)))
|
||||
(goto-char position)
|
||||
(error "Cannot position in data `%s'" data)))
|
||||
|
||||
(defvar prot-eww--occur-feed-regexp
|
||||
(concat "\\(rss\\|atom\\)\\+xml.\\(.\\|\n\\)"
|
||||
".*href=[\"']\\(.*?\\)[\"']")
|
||||
"Regular expression to match web feeds in HTML source.")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-find-feed ()
|
||||
"Produce bespoke buffer with RSS/Atom links from XML source."
|
||||
(interactive)
|
||||
(interactive nil eww-mode)
|
||||
(unless (derived-mode-p 'eww-mode)
|
||||
(user-error "This command only works in an EWW buffer"))
|
||||
(let* ((url (or (plist-get eww-data :start)
|
||||
(plist-get eww-data :contents)
|
||||
(plist-get eww-data :home)
|
||||
|
|
@ -401,124 +120,12 @@ consider whole buffer."
|
|||
(let ((inhibit-read-only t)
|
||||
(base-url (replace-regexp-in-string "\\(.*/\\)[^/]+\\'" "\\1" url)))
|
||||
(goto-char (point-min))
|
||||
(unless (re-search-forward prot-common-url-regexp nil t)
|
||||
(re-search-forward ".*")
|
||||
(replace-match (concat base-url "\\&"))))))))
|
||||
|
||||
;;TODO: Add this variable as user-option, that is, define it with
|
||||
;;`defcustom' so that users can use the customization interface to
|
||||
;;modify it.
|
||||
|
||||
(defvar prot-eww-search-engines
|
||||
'((debbugs . (debbugs
|
||||
"https://debbugs.gnu.org/cgi/bugreport.cgi?bug="
|
||||
hist-var prot-eww--debbugs-hist))
|
||||
(wikipedia . (wikipedia
|
||||
"https://en.m.wikipedia.org/w/index.php?search="
|
||||
hist-var prot-eww--wikipedia-hist))
|
||||
(archwiki . (archwiki
|
||||
"https://wiki.archlinux.org/index.php?search="
|
||||
hist-var prot-eww--archwiki-hist))
|
||||
(aur . (aur "https://aur.archlinux.org/packages/?K="
|
||||
hist-var prot-eww--aur-hist)))
|
||||
"Alist of Plist of web search engines related data.
|
||||
From now on refer to this type of data as APLIST. Each element
|
||||
of APLIST is (KEY . VALUE) pair. KEY is a symbol specifying
|
||||
search engine name. The VALUE is property list.
|
||||
|
||||
The plist has two key-value pairs. K1 is the same symbol has KEY
|
||||
and V1 is search string of the search engine.
|
||||
|
||||
K2 is the symbol 'hist-var', V2 is also a symbol that has a format
|
||||
'prot-eww--K1-hist'.
|
||||
|
||||
NOTE: If you modify this variable after `prot-eww' is loaded you
|
||||
need to run the following code after modification:
|
||||
|
||||
(prot-eww--define-hist-var prot-eww-search-engines)")
|
||||
|
||||
;; Below 's-string' is short for 'search-string'. For wikipedia which
|
||||
;; is this string: "https://en.m.wikipedia.org/w/index.php?search=". I
|
||||
;; use this name because I don't know it's proper name.
|
||||
|
||||
;; Define constructor and selectors functions to access
|
||||
;; `prot-eww-search-engines'.
|
||||
;; the constructor
|
||||
(defun prot-eww--cons-search-engines (name s-string)
|
||||
"Include a new Alist element.
|
||||
The alist element is added to variable `prot-eww-search-engines'.
|
||||
|
||||
NAME should be symbol representing the search engine. S-STRING
|
||||
should be string, which is specific to named search engine."
|
||||
(let ((my-plist `(,name ,s-string))
|
||||
(hist-var-name (format "prot-eww--%s-hist"
|
||||
(symbol-name name))))
|
||||
(plist-put my-plist 'hist-var (intern hist-var-name))
|
||||
(let ((my-alist (cons name my-plist)))
|
||||
(add-to-list 'prot-eww-search-engines my-alist))))
|
||||
|
||||
;; Selectors definitions start
|
||||
(defun prot-eww--select-hist-name (aplist engine-name)
|
||||
"Get hist-var-name from APLIST of ENGINE-NAME."
|
||||
(let ((hist-var-name (plist-get
|
||||
(alist-get engine-name aplist)
|
||||
'hist-var)))
|
||||
hist-var-name))
|
||||
|
||||
(defun prot-eww--select-engine-names (aplist)
|
||||
"Return a list of search-engine names from APLIST.
|
||||
Each value of the list is a string."
|
||||
(mapcar (lambda (x) (format "%s" (car x)))
|
||||
aplist))
|
||||
|
||||
(defun prot-eww--select-s-string (aplist engine-name)
|
||||
"Return the search-string for specified ENGINE-NAME from APLIST."
|
||||
(plist-get
|
||||
(alist-get engine-name aplist)
|
||||
engine-name))
|
||||
;; Selector definitions end here.
|
||||
|
||||
(defun prot-eww--define-hist-var (aplist)
|
||||
"Initialize APLIST hist-variables to empty list; return nil."
|
||||
(let ((engine-names
|
||||
(prot-eww--select-engine-names aplist)))
|
||||
(dolist (engine engine-names)
|
||||
(let ((hist-var-name
|
||||
(prot-eww--select-hist-name aplist
|
||||
(intern engine))))
|
||||
(set hist-var-name '())))))
|
||||
|
||||
(prot-eww--define-hist-var prot-eww-search-engines)
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-search-engine (engine s-term &optional arg)
|
||||
"Search S-TERM using ENGINE.
|
||||
ENGINE is an assossiation defined in `prot-eww-search-engines'.
|
||||
|
||||
With optional prefix ARG (\\[universal-argument]) open the search
|
||||
result in a new buffer."
|
||||
(interactive
|
||||
(let* ((engine-list (prot-eww--select-engine-names
|
||||
prot-eww-search-engines))
|
||||
(engine-name (completing-read
|
||||
"Search with: " engine-list nil t nil
|
||||
'prot-eww--engine-hist))
|
||||
(history-list (prot-eww--select-hist-name
|
||||
prot-eww-search-engines
|
||||
(intern engine-name)))
|
||||
(search-term (read-string
|
||||
"Search for: " nil history-list)))
|
||||
(list engine-name search-term
|
||||
(prefix-numeric-value current-prefix-arg))))
|
||||
(let* ((s-string
|
||||
(prot-eww--select-s-string prot-eww-search-engines
|
||||
(intern engine)))
|
||||
(eww-pass (format "%s%s" s-string s-term))
|
||||
(history-list (prot-eww--select-hist-name
|
||||
prot-eww-search-engines
|
||||
(intern engine))))
|
||||
(add-to-history history-list s-term)
|
||||
(eww eww-pass arg)))
|
||||
(while (< (point) (point-max))
|
||||
(goto-char (line-beginning-position))
|
||||
(when (and (looking-at prot-common-url-regexp)
|
||||
(not (looking-at (format "%s.*" url))))
|
||||
(insert base-url))
|
||||
(forward-line 1)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-open-in-other-window ()
|
||||
|
|
@ -527,134 +134,5 @@ result in a new buffer."
|
|||
(other-window-prefix) ; For emacs28 -- it's a hack, but why not?
|
||||
(eww-open-in-new-buffer))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-readable ()
|
||||
"Use more opinionated `eww-readable'.
|
||||
|
||||
Set width is set to `current-fill-column'. Adjust size of
|
||||
images."
|
||||
(interactive)
|
||||
(let ((shr-width (current-fill-column))
|
||||
(shr-max-image-proportion 0.35))
|
||||
(eww-readable)))
|
||||
|
||||
;; NOTE 2021-09-08: This uses the EWW-specific bookmarks, NOT those of
|
||||
;; bookmark.el. Further below I provide integration with the latter,
|
||||
;; meaning that we must either make this obsolete or make it work with
|
||||
;; the new system.
|
||||
;;;###autoload
|
||||
(defun prot-eww-bookmark-page (title)
|
||||
"Add eww bookmark named with TITLE."
|
||||
(interactive
|
||||
(list
|
||||
(read-string "Set bookmark title: " (plist-get eww-data :title))))
|
||||
(plist-put eww-data :title title)
|
||||
(eww-add-bookmark))
|
||||
|
||||
(defvar prot-eww--punctuation-regexp "[][{}!@#$%^&*()_=+'\"?,.\|;:~`‘’“”]*"
|
||||
"Regular expression of punctionation that should be removed.")
|
||||
|
||||
(defun prot-eww--slug-no-punct (str)
|
||||
"Convert STR to a file name slug."
|
||||
(replace-regexp-in-string prot-eww--punctuation-regexp "" str))
|
||||
|
||||
(defun prot-eww--slug-hyphenate (str)
|
||||
"Replace spaces with hyphens in STR.
|
||||
Also replace multiple hyphens with a single one and remove any
|
||||
trailing hyphen."
|
||||
(replace-regexp-in-string
|
||||
"-$" ""
|
||||
(replace-regexp-in-string
|
||||
"-\\{2,\\}" "-"
|
||||
(replace-regexp-in-string "--+\\|\s+" "-" str))))
|
||||
|
||||
(defun prot-eww--sluggify (str)
|
||||
"Make STR an appropriate file name slug."
|
||||
(downcase (prot-eww--slug-hyphenate (prot-eww--slug-no-punct str))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-download-html (name)
|
||||
"Download web page and call the file with NAME."
|
||||
(interactive
|
||||
(list
|
||||
(prot-eww--sluggify
|
||||
(read-string "Set downloaded file name: " (plist-get eww-data :title)))))
|
||||
(let* ((path (thread-last eww-download-directory
|
||||
(expand-file-name
|
||||
(concat (format-time-string "%Y%m%d_%H%M%S") "--" name ".html"))))
|
||||
(out (prot-common-shell-command-with-exit-code-and-output
|
||||
"wget" "-q" (format "%s" (plist-get eww-data :url))
|
||||
"-O" (format "%s" (shell-quote-argument path)))))
|
||||
(if (= (car out) 0)
|
||||
(message "Downloaded page at %s" path)
|
||||
(message "Error downloading page: %s" (cdr out)))))
|
||||
|
||||
(defun prot-eww--kill-buffers-when (predicate)
|
||||
"Kill buffers when PREDICATE is non-nil.
|
||||
|
||||
Loop through the buffer list, calling PREDICATE with each buffer.
|
||||
When calling PREDICATE with a buffer returns non-nil, kill that
|
||||
buffer.
|
||||
|
||||
PREDICATE must be function that takes buffer-object as the one
|
||||
and only argument. It should return nil or non-nil."
|
||||
(let ((list-buffers (buffer-list)))
|
||||
(dolist (buffer list-buffers)
|
||||
(when (funcall predicate buffer)
|
||||
(kill-buffer buffer)))))
|
||||
|
||||
(defun prot-eww--kill-eww-buffers-p (buffer)
|
||||
"Predicate function. Return nil or non-nil.
|
||||
|
||||
Take BUFFER, make it current, check if it has 'eww-mode' as the
|
||||
`major-mode' or if its major mode is derived from `special-mode'
|
||||
and has \"eww\" in the buffer-name. Then return non-nil."
|
||||
(let ((case-fold-search t)) ; ignore case
|
||||
(with-current-buffer buffer
|
||||
(or (eq major-mode 'eww-mode)
|
||||
(and (derived-mode-p 'special-mode)
|
||||
(string-match "\\*.*eww.*\\*" (buffer-name)))))))
|
||||
|
||||
(defun prot-eww-kill-eww-buffers ()
|
||||
"Kill all EWW buffers.
|
||||
Also kill special buffers made by EWW for example buffers like
|
||||
\"*eww-bookmarks*\", \"*eww-history*\" etc."
|
||||
(prot-eww--kill-buffers-when 'prot-eww--kill-eww-buffers-p))
|
||||
|
||||
(defcustom prot-eww-delete-cookies t
|
||||
"If non-nil delete cookies when `prot-eww-quit' is called."
|
||||
:type 'boolean
|
||||
:group 'prot-eww)
|
||||
|
||||
(defun prot-eww-delete-cookies ()
|
||||
"Delete cookies from the cookie file."
|
||||
(when prot-eww-delete-cookies
|
||||
(url-cookie-delete-cookies)))
|
||||
|
||||
;; TODO: Make it defcustom
|
||||
(defvar prot-eww-quit-hook nil
|
||||
"Run this hook when `prot-eww-quit' is called.")
|
||||
|
||||
;; Populate the hook with these functions.
|
||||
(dolist (func '(prot-eww-delete-cookies
|
||||
prot-eww-kill-eww-buffers
|
||||
prot-eww--save-visited-history))
|
||||
(add-hook 'prot-eww-quit-hook func))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-eww-quit ()
|
||||
"Quit eww, kill all its buffers, delete all cookies.
|
||||
As a final step, save `prot-eww-visited-history' to a file (see
|
||||
`prot-eww-save-history-file')."
|
||||
(interactive)
|
||||
(if prot-eww-save-visited-history
|
||||
(when (y-or-n-p "Are you sure you want to quit eww? ")
|
||||
(run-hooks 'prot-eww-quit-hook))
|
||||
;;
|
||||
;; Now users have full control what `prot-eww-quit' does, by
|
||||
;; modifying `prot-eww-quit-hook'.
|
||||
(when (yes-or-no-p "Are you sure you want to quit eww?")
|
||||
(run-hooks 'prot-eww-quit-hook))))
|
||||
|
||||
(provide 'prot-eww)
|
||||
;;; prot-eww.el ends here
|
||||
|
|
|
|||
363
emacs/.emacs.d/prot-lisp/prot-icons.el
Normal file
363
emacs/.emacs.d/prot-lisp/prot-icons.el
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
;;; prot-icons.el --- Get characters, icons, and symbols for things -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2025-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or (at
|
||||
;; your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Extensions to get icons and symbols for files, buffers, or related.
|
||||
;; Intended for my Emacs setup: <https://protesilaos.com/emacs/dotemacs/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'prot-common)
|
||||
|
||||
(defgroup prot-icons nil
|
||||
"Get characters, icons, and symbols for things."
|
||||
:group 'convenience)
|
||||
|
||||
(defcustom prot-icons-style 'symbolic
|
||||
"The style of the icons.
|
||||
|
||||
- `alphabetic' expresses icons using alphabetic characters.
|
||||
- `emoji' represents icons as emoji characters.
|
||||
- `symbolic' uses Unicode symbols for the icons.
|
||||
|
||||
Any other value has the same meaning as `text'."
|
||||
:type '(choice
|
||||
(const :tag "Alphabetic characters" alphabetic)
|
||||
(const :tag "Emoji characters" emoji)
|
||||
(const :tag "Unicode symbols" symbolic)))
|
||||
|
||||
(defface prot-icons-icon
|
||||
'((t :inherit (bold fixed-pitch)))
|
||||
"Basic attributes for an icon."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-red
|
||||
'((default :inherit prot-icons-icon)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#aa3232")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#f06464")
|
||||
(t :foreground "red"))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-green
|
||||
'((default :inherit prot-icons-icon)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#107010")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#33bb33")
|
||||
(t :foreground "green"))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-yellow
|
||||
'((default :inherit prot-icons-icon)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#605000")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#e0a055")
|
||||
(t :foreground "yellow"))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-blue
|
||||
'((default :inherit prot-icons-icon)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#223399")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#5599ff")
|
||||
(t :foreground "blue"))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-magenta
|
||||
'((default :inherit prot-icons-icon)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#8f2270")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#ee70aa")
|
||||
(t :foreground "magenta"))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-cyan
|
||||
'((default :inherit prot-icons-icon)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "#226067")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "#77b0c0")
|
||||
(t :foreground "cyan"))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-gray
|
||||
'((default :inherit prot-icons-icon)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
:foreground "gray30")
|
||||
(((class color) (min-colors 88) (background dark))
|
||||
:foreground "gray70")
|
||||
(t :foreground "gray"))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defface prot-icons-directory
|
||||
'((t :inherit (prot-icons-icon dired-directory)))
|
||||
"Face for icons."
|
||||
:group 'prot-icons)
|
||||
|
||||
(defvar prot-icons-alphabetic
|
||||
'((dired-mode "|*" prot-icons-directory)
|
||||
(archive-mode "|@" prot-icons-directory)
|
||||
(diff-mode ">Δ" prot-icons-yellow) ; διαφορά
|
||||
(prog-mode ">Π" prot-icons-magenta) ; πρόγραμμα
|
||||
(conf-mode ">Π" prot-icons-gray) ; πρόγραμμα
|
||||
(text-mode ">Α" prot-icons-green) ; αλφάβητο
|
||||
(comint-mode ">_" prot-icons-gray)
|
||||
(read-only "" prot-icons-red)
|
||||
(document ">Σ" prot-icons-red) ; σύγγραμμα
|
||||
(audio ">Η" prot-icons-cyan) ; ήχος
|
||||
(image ">Ε" prot-icons-yellow) ; εικόνα
|
||||
(video ">Κ" prot-icons-blue) ; κίνηση (κινηματογράφος)
|
||||
(frame "[]" prot-icons-gray)
|
||||
(git "|-" prot-icons-gray)
|
||||
(t ">." prot-icons-gray))
|
||||
"Major modes or concepts and their corresponding icons.
|
||||
Each element is a cons cell of the form (THING STRING FACE), where THING
|
||||
is a symbol STRING is one or more characters that represent THING, and
|
||||
FACE is the face to use for it, where applicable.")
|
||||
|
||||
(defvar prot-icons-emoji
|
||||
'((dired-mode "📁" prot-icons-directory)
|
||||
(archive-mode "💼" prot-icons-directory)
|
||||
(diff-mode "🟰" prot-icons-yellow)
|
||||
(prog-mode "🧬" prot-icons-magenta)
|
||||
(conf-mode "📝" prot-icons-gray)
|
||||
(text-mode "💬" prot-icons-green)
|
||||
(comint-mode "🔮" prot-icons-gray)
|
||||
(read-only "🔒" prot-icons-red)
|
||||
(document "📜" prot-icons-red)
|
||||
(audio "🔊" prot-icons-cyan)
|
||||
(image "🎨" prot-icons-yellow)
|
||||
(video "📹" prot-icons-blue)
|
||||
(frame "🏠" prot-icons-gray)
|
||||
(git "" prot-icons-gray)
|
||||
(t "🔸" prot-icons-gray))
|
||||
"Same as `prot-icons-alphabetic' with emoji characters.")
|
||||
|
||||
(defvar prot-icons-symbolic
|
||||
'((dired-mode "🖿" prot-icons-directory)
|
||||
(archive-mode "🮯" prot-icons-directory)
|
||||
(diff-mode "⇄" prot-icons-yellow)
|
||||
(prog-mode "⨍" prot-icons-magenta)
|
||||
(conf-mode "🗅" prot-icons-gray)
|
||||
(text-mode "¶" prot-icons-green)
|
||||
(comint-mode "⨠" prot-icons-gray)
|
||||
(read-only "" prot-icons-red)
|
||||
(document "🗎" prot-icons-red)
|
||||
(audio "𝅘𝅥𝅮" prot-icons-cyan)
|
||||
(image "" prot-icons-yellow)
|
||||
(video "▶" prot-icons-blue)
|
||||
(frame "" prot-icons-gray)
|
||||
(git "⇅" prot-icons-gray)
|
||||
(t "·" prot-icons-gray))
|
||||
"Same as `prot-icons-alphabetic' with Unicode symbols.")
|
||||
|
||||
(defun prot-icons--get-style ()
|
||||
"Return icons corresponding to `prot-icons-style'.
|
||||
More specifically, return the value of `prot-icons-alphabetic' or
|
||||
`prot-icons-emoji'."
|
||||
(pcase prot-icons-style
|
||||
('emoji prot-icons-emoji)
|
||||
('symbolic prot-icons-symbolic)
|
||||
(_ prot-icons-alphabetic)))
|
||||
|
||||
(defun prot-icons--get (thing)
|
||||
"Return `prot-icons-alphabetic' representation of THING."
|
||||
(unless (symbolp thing)
|
||||
(error "The thing `%s' is not a symbol" thing))
|
||||
(let ((icons (prot-icons--get-style)))
|
||||
(when (string-suffix-p "-mode" (symbol-name thing))
|
||||
(while-let ((parent (get thing 'derived-mode-parent)))
|
||||
(setq thing parent)))
|
||||
(or (alist-get thing icons)
|
||||
(alist-get t icons))))
|
||||
|
||||
(defun prot-icons-get-icon (thing &optional face)
|
||||
"Return propertized icon THING."
|
||||
(pcase-let* ((`(,icon ,inherent-face) (prot-icons--get thing))
|
||||
(face (or face inherent-face)))
|
||||
(format "%2s" (propertize icon 'font-lock-face face 'face face))))
|
||||
|
||||
(defun prot-icons-get-file-icon (file)
|
||||
"Return FILE icon and face."
|
||||
(cond
|
||||
((null file)
|
||||
(prot-icons-get-icon nil))
|
||||
((string-suffix-p "/" file)
|
||||
(prot-icons-get-icon 'dired-mode))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'archive) file)
|
||||
(prot-icons-get-icon 'archive-mode))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'text) file)
|
||||
(prot-icons-get-icon 'text-mode))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'image) file)
|
||||
(prot-icons-get-icon 'image))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'audio) file)
|
||||
(prot-icons-get-icon 'audio))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'video) file)
|
||||
(prot-icons-get-icon 'video))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'document) file)
|
||||
(prot-icons-get-icon 'document))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'diff) file)
|
||||
(prot-icons-get-icon 'diff-mode))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'program) file)
|
||||
(prot-icons-get-icon 'prog-mode))
|
||||
((string-match-p (prot-common--get-file-type-regexp 'program-data) file)
|
||||
(prot-icons-get-icon 'conf-mode))
|
||||
(t (prot-icons-get-icon nil))))
|
||||
|
||||
;;;; Icons for Dired
|
||||
|
||||
;; Adapted from `nerd-icons-dired'
|
||||
|
||||
(defun prot-icons-dired--add-overlay (pos string)
|
||||
"Add overlay to display STRING at POS."
|
||||
(let ((overlay (make-overlay (1- pos) pos)))
|
||||
(overlay-put overlay 'prot-icons-overlay t)
|
||||
(overlay-put overlay 'evaporate t)
|
||||
(overlay-put overlay 'before-string (propertize string 'display string))
|
||||
(overlay-put overlay 'after-string (propertize " " 'display '(space :align-to 1)))))
|
||||
|
||||
(defun prot-icons-dired--remove-all-overlays ()
|
||||
"Remove all `prot-icons' overlays."
|
||||
(dolist (buffer (buffer-list))
|
||||
(when (and (derived-mode-p 'dired-mode) prot-icons-dired-mode)
|
||||
(save-restriction
|
||||
(widen)
|
||||
(remove-overlays nil nil 'prot-icons-overlay t)))))
|
||||
|
||||
(defun prot-icons-dired--annotate ()
|
||||
"Add icons to all files in the visible region of the buffer."
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (and (dired-next-line 1) (not (eobp)))
|
||||
(when-let* ((file (dired-get-filename nil :no-error))
|
||||
(icon (if (file-directory-p file)
|
||||
(prot-icons-get-file-icon (concat file "/"))
|
||||
(prot-icons-get-file-icon file))))
|
||||
(prot-icons-dired--add-overlay (dired-move-to-filename) icon)))))
|
||||
|
||||
(defun prot-icons-dired--refresh (&rest _)
|
||||
"Update the display of icons of files in a Dired buffer."
|
||||
(prot-icons-dired--remove-all-overlays)
|
||||
(save-restriction
|
||||
(widen)
|
||||
(prot-icons-dired--annotate)))
|
||||
|
||||
(defun prot-icons-dired--setup ()
|
||||
"Set up Dired to display icons."
|
||||
(setq-local tab-width 1)
|
||||
(prot-icons-dired--refresh))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-icons-dired-mode
|
||||
"Display icons for Dired entries."
|
||||
:global t
|
||||
(if prot-icons-dired-mode
|
||||
(progn
|
||||
(add-hook 'dired-mode-hook #'prot-icons-dired--setup)
|
||||
(add-hook 'dired-after-readin-hook 'prot-icons-dired--annotate)
|
||||
(advice-add #'dired-do-redisplay :after #'prot-icons-dired--refresh)
|
||||
(advice-add #'wdired-abort-changes :after #'prot-icons-dired--refresh))
|
||||
(prot-icons-dired--remove-all-overlays)
|
||||
(remove-hook 'dired-mode-hook #'prot-icons-dired--setup)
|
||||
(remove-hook 'dired-after-readin-hook 'prot-icons-dired--annotate)
|
||||
(advice-remove #'dired-do-redisplay #'prot-icons-dired--refresh)
|
||||
(advice-remove #'wdired-abort-changes #'prot-icons-dired--refresh)))
|
||||
|
||||
;;;; Icons for Xref
|
||||
|
||||
;; Adapted from `nerd-icons-xref'
|
||||
|
||||
(defun prot-icons-xref--add-overlay (position string)
|
||||
"Add overlay at POSITION to display STRING."
|
||||
(let ((overlay (make-overlay position (+ position 1))))
|
||||
(overlay-put overlay 'prot-icons-overlay t)
|
||||
(overlay-put overlay 'evaporate t)
|
||||
(overlay-put overlay 'before-string (format "%s " (propertize string 'display string)))))
|
||||
|
||||
(defun prot-icons-xref--add-icons ()
|
||||
"Add icons to Xref headings."
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let ((prop))
|
||||
(while (setq prop (text-property-search-forward 'xref-group))
|
||||
(when-let* ((start (prop-match-beginning prop))
|
||||
(end (prop-match-end prop))
|
||||
(file (string-chop-newline (buffer-substring-no-properties start end)))
|
||||
(icon (prot-icons-get-file-icon file)))
|
||||
(prot-icons-xref--add-overlay start icon))))))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-icons-xref-mode
|
||||
"Display icons for Xref headings."
|
||||
:global t
|
||||
(if prot-icons-xref-mode
|
||||
(add-hook 'xref-after-update-hook #'prot-icons-xref--add-icons)
|
||||
(remove-hook 'xref-after-update-hook #'prot-icons-xref--add-icons)))
|
||||
|
||||
;;;; Icons for Buffer menu
|
||||
|
||||
(defun prot-icons-buffer-menu--add-overlay (position string)
|
||||
"Add overlay at POSITION to display STRING."
|
||||
(let ((overlay (make-overlay position (+ position 1))))
|
||||
(overlay-put overlay 'prot-icons-overlay t)
|
||||
(overlay-put overlay 'evaporate t)
|
||||
(overlay-put overlay 'before-string (propertize string 'display string))))
|
||||
|
||||
(defun prot-icons-buffer-menu--add-icons (&rest _)
|
||||
"Add icons to `Buffer-menu-mode' entries."
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
(when (derived-mode-p 'Buffer-menu-mode)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while-let ((match (text-property-search-forward 'tabulated-list-id))
|
||||
(buffer (prop-match-value match))
|
||||
(mode (with-current-buffer buffer major-mode))
|
||||
(icon (prot-icons-get-icon mode)))
|
||||
(prot-icons-buffer-menu--add-overlay (line-beginning-position) (concat icon " "))))))))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-icons-buffer-menu-mode
|
||||
"Display icons for `Buffer-menu-mode' entries."
|
||||
:global t
|
||||
(if prot-icons-buffer-menu-mode
|
||||
(advice-add #'list-buffers--refresh :after #'prot-icons-buffer-menu--add-icons)
|
||||
(advice-remove #'list-buffers--refresh #'prot-icons-buffer-menu--add-icons)))
|
||||
|
||||
(provide 'prot-icons)
|
||||
;;; prot-icons.el ends here
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-marginalia.el --- Code for my custom mode line -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -56,7 +56,7 @@
|
|||
|
||||
(defun prot-marginalia-buffer (buffer)
|
||||
"Annotate BUFFER with the return value of function `buffer-file-name'."
|
||||
(if-let ((name (buffer-file-name (get-buffer buffer))))
|
||||
(if-let* ((name (buffer-file-name (get-buffer buffer))))
|
||||
(prot-marginalia-display (abbreviate-file-name name))
|
||||
(prot-marginalia-display (format "%s" (buffer-local-value 'major-mode (get-buffer buffer))))))
|
||||
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
(pkg (intern-soft package))
|
||||
(desc (or (when (package-desc-p pkg) pkg)
|
||||
(car (alist-get pkg pkg-alist))
|
||||
(if-let (built-in (assq pkg package--builtins))
|
||||
(if-let* ((built-in (assq pkg package--builtins)))
|
||||
(package--from-builtin built-in)
|
||||
(car (alist-get pkg package-archive-contents))))))
|
||||
(prot-marginalia-display (package-desc-summary desc))))
|
||||
|
|
@ -81,15 +81,15 @@
|
|||
|
||||
(defun prot-marginalia--first-line-documentation (symbol)
|
||||
"Return first line of SYMBOL documentation string."
|
||||
(when-let ((doc-string (prot-marginalia--get-symbol-doc symbol))
|
||||
((stringp doc-string))
|
||||
((not (string-empty-p doc-string))))
|
||||
(when-let* ((doc-string (prot-marginalia--get-symbol-doc symbol))
|
||||
((stringp doc-string))
|
||||
((not (string-empty-p doc-string))))
|
||||
(car (split-string doc-string "[?!.\n]"))))
|
||||
|
||||
(defun prot-marginalia-symbol (symbol)
|
||||
"Annotate SYMBOL with its documentation string."
|
||||
(when-let ((sym (intern-soft symbol))
|
||||
(doc-string (prot-marginalia--first-line-documentation sym)))
|
||||
(when-let* ((sym (intern-soft symbol))
|
||||
(doc-string (prot-marginalia--first-line-documentation sym)))
|
||||
(prot-marginalia-display doc-string)))
|
||||
|
||||
(provide 'prot-marginalia)
|
||||
|
|
|
|||
411
emacs/.emacs.d/prot-lisp/prot-minibuffer.el
Normal file
411
emacs/.emacs.d/prot-lisp/prot-minibuffer.el
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
;;; prot-minibuffer.el --- Extensions for the minibuffer and completions -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2025-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; Maintainer: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or (at
|
||||
;; your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Extensions for the minibuffer and completions, intended for my
|
||||
;; Emacs setup: <https://protesilaos.com/emacs/dotemacs/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'prot-icons)
|
||||
|
||||
(defgroup prot-minibuffer nil
|
||||
"Extensions for the minibuffer and completions."
|
||||
:group 'minibuffer)
|
||||
|
||||
;;;; Completion category grouping, sorting, and affixating
|
||||
|
||||
;; Add some missing completion categories to let me configure the
|
||||
;; relevant prompts via the `completion-category-overrides'.
|
||||
(defun prot-minibuffer@read-from-kill-ring (&rest args)
|
||||
(let ((completion-extra-properties (list :category 'prot-minibuffer-kill-ring)))
|
||||
(apply args)))
|
||||
|
||||
(defun prot-minibuffer@read-library-name (&rest args)
|
||||
(let ((completion-extra-properties (list :category 'prot-minibuffer-library)))
|
||||
(apply args)))
|
||||
|
||||
(defun prot-minibuffer@emoji--read-emoji (&rest args)
|
||||
(let ((completion-extra-properties (list :category 'prot-minibuffer-emoji)))
|
||||
(apply args)))
|
||||
|
||||
(defun prot-minibuffer@password-store--completing-read (&rest args)
|
||||
(let ((completion-extra-properties (list :category 'prot-minibuffer-pass)))
|
||||
(apply args)))
|
||||
|
||||
(defun prot-minibuffer@read-input-method-name (&rest args)
|
||||
(let ((completion-extra-properties (list :category 'prot-minibuffer-input-method)))
|
||||
(apply args)))
|
||||
|
||||
(defun prot-minibuffer--do-advice (advice functions place)
|
||||
"Handle the advice installed by the function `prot-minibuffer-missing-categories-mode'.
|
||||
For each function in FUNCTIONS apply ARGS."
|
||||
(dolist (original functions)
|
||||
(when-let* ((my-function-name (format "prot-minibuffer@%s" original))
|
||||
(my-function-symbol (intern-soft my-function-name))
|
||||
(args (if place
|
||||
(list advice original place my-function-symbol)
|
||||
(list advice original my-function-symbol))))
|
||||
(apply args))))
|
||||
|
||||
(defvar prot-minibuffer-missing-completion-categories-symbols
|
||||
'( read-from-kill-ring read-library-name emoji--read-emoji
|
||||
read-input-method-name password-store--completing-read)
|
||||
"Symbols to be advised by `prot-minibuffer-missing-categories-mode'.")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-minibuffer-missing-categories-mode
|
||||
"When enabled, add missing compleiton categories to relevant prompts."
|
||||
:global t
|
||||
(when-let* ((functions prot-minibuffer-missing-completion-categories-symbols))
|
||||
(if prot-minibuffer-missing-categories-mode
|
||||
(prot-minibuffer--do-advice #'advice-add functions :around)
|
||||
(prot-minibuffer--do-advice #'advice-remove functions nil))))
|
||||
|
||||
(defun prot-minibuffer-file-sort (files)
|
||||
"Sort FILES to have directories first and the rest alphabetically.
|
||||
Omit the .. directory from FILES."
|
||||
(setq files (delete "../" files))
|
||||
(setq files (minibuffer-sort-alphabetically files))
|
||||
(let ((directory-p (lambda (file) (string-suffix-p "/" file))))
|
||||
(nconc (seq-filter directory-p files)
|
||||
(seq-remove directory-p files))))
|
||||
|
||||
(defun prot-minibuffer-file-affixate (files)
|
||||
"Return FILES with prefix and suffix."
|
||||
(mapcar
|
||||
(lambda (file)
|
||||
(list file (format "%s " (prot-icons-get-file-icon file)) ""))
|
||||
files))
|
||||
|
||||
(defun prot-minibuffer-file-group (file transform)
|
||||
"Return FILE group name unless TRANSFORM is non-nil."
|
||||
(cond
|
||||
(transform file)
|
||||
((string-suffix-p "/" file) "Directories")
|
||||
((string-prefix-p "." file) "Dotfiles")
|
||||
((when-let* ((extension (file-name-extension file :include-dot))
|
||||
(_ (not (string-blank-p extension))))
|
||||
extension))
|
||||
(t "Other files")))
|
||||
|
||||
(defun prot-minibuffer--set-default-sort (candidates)
|
||||
"Sort CANDIDATES according to `completions-sort' and return the sorted list."
|
||||
(setq candidates
|
||||
(pcase completions-sort
|
||||
('nil candidates)
|
||||
('alphabetical (minibuffer-sort-alphabetically candidates))
|
||||
('historical (minibuffer-sort-by-history candidates))
|
||||
(_ (funcall completions-sort candidates)))))
|
||||
|
||||
(defun prot-minibuffer-symbol-sort (symbols)
|
||||
"Sort SYMBOLS so that public ones come first."
|
||||
(setq symbols (prot-minibuffer--set-default-sort symbols))
|
||||
(let ((private-p (lambda (symbol) (string-match-p "--" symbol))))
|
||||
(nconc (seq-remove private-p symbols)
|
||||
(seq-filter private-p symbols))))
|
||||
|
||||
(defun prot-minibuffer-symbol-group (symbol-name transform)
|
||||
"Return SYMBOL-NAME group unless TRANSFORM is non-nil."
|
||||
(let ((first-word-fn (lambda (string)
|
||||
(if (string-match "\\(.*?\\)[@/-].*" string)
|
||||
(match-string 1 string)
|
||||
string))))
|
||||
(cond
|
||||
(transform symbol-name)
|
||||
((funcall first-word-fn symbol-name)))))
|
||||
|
||||
(defun prot-minibuffer--propertize-suffix-with-space (string)
|
||||
"Propertize STRING with spacing before it."
|
||||
(format " %s%s"
|
||||
(if (and (or (eq completions-format 'horizontal)
|
||||
(eq completions-format 'vertical))
|
||||
(not prot-emacs-completion-ui))
|
||||
" "
|
||||
(propertize " " 'display '(space :align-to 60)))
|
||||
(propertize string 'face 'completions-annotations)))
|
||||
|
||||
(defun prot-minibuffer-buffer-sort (buffers)
|
||||
"Sort BUFFERS by visibility.
|
||||
This is a copy of `beframe-buffer-sort-visibility', from my `beframe'
|
||||
package."
|
||||
(let* ((bufs (seq-group-by
|
||||
(lambda (buf)
|
||||
(cond
|
||||
((eq buf (current-buffer)) :current)
|
||||
((get-buffer-window buf 'visible) :visible)
|
||||
(t :hidden)))
|
||||
buffers))
|
||||
(hidden (alist-get :hidden bufs))
|
||||
(visible (alist-get :visible bufs))
|
||||
(current (alist-get :current bufs)))
|
||||
(nconc hidden visible current)))
|
||||
|
||||
;; NOTE 2026-07-25: I am disabling this because it messes up with the sorting.
|
||||
|
||||
;; (defun prot-minibuffer-buffer-group (buffer-name transform)
|
||||
;; "Return BUFFER-NAME group name unless TRANSFORM is non-nil."
|
||||
;; (cond
|
||||
;; (transform buffer-name)
|
||||
;; ((string-prefix-p "*" buffer-name) "Special")
|
||||
;; ((string-match-p "\\`magit.*?:" buffer-name) "Git")
|
||||
;; ((if-let* ((buffer (get-buffer buffer-name)))
|
||||
;; (with-current-buffer buffer
|
||||
;; (cond
|
||||
;; ((derived-mode-p 'dired-mode)
|
||||
;; "Directory")
|
||||
;; ((derived-mode-p 'prog-mode)
|
||||
;; "Program")
|
||||
;; ((derived-mode-p 'text-mode)
|
||||
;; "Prose")
|
||||
;; (t
|
||||
;; (format "%s" major-mode))))
|
||||
;; ""))))
|
||||
|
||||
(defun prot-minibuffer-buffer-affixate (buffers)
|
||||
"Return BUFFERS with prefix and suffix."
|
||||
(mapcar
|
||||
(lambda (buffer)
|
||||
(let* ((buffer-object (get-buffer buffer))
|
||||
(mode (with-current-buffer buffer-object major-mode)))
|
||||
(list
|
||||
buffer
|
||||
(format "%s " (prot-icons-get-icon mode))
|
||||
(prot-minibuffer--propertize-suffix-with-space (format "%s" mode)))))
|
||||
buffers))
|
||||
|
||||
(defun prot-minibuffer-bookmark-affixate (bookmarks)
|
||||
"Return BOOKMARKS with prefix and suffix."
|
||||
(mapcar
|
||||
(lambda (bookmark)
|
||||
(let* ((data (bookmark-get-bookmark bookmark))
|
||||
(file (bookmark-prop-get data 'filename)))
|
||||
(list
|
||||
bookmark
|
||||
(format "%s " (prot-icons-get-file-icon file))
|
||||
(prot-minibuffer--propertize-suffix-with-space (format "%s" file)))))
|
||||
bookmarks))
|
||||
|
||||
(defun prot-minibuffer-library-sort (libraries)
|
||||
"Sort LIBRARIES, omitting autoloads and bytecode files."
|
||||
(setq libraries (seq-remove
|
||||
(lambda (library)
|
||||
(string-match-p "\\(-autoload\\|\\.elc\\|\\.dir-locals\\)" library))
|
||||
libraries))
|
||||
(prot-minibuffer--set-default-sort libraries))
|
||||
|
||||
(defcustom prot-minibuffer-used-input-methods
|
||||
'("greek" "french-postfix" "spanish-postfix")
|
||||
"Input methods that I use."
|
||||
:type '(choice (const nil)
|
||||
(repeat mule-input-method-string)))
|
||||
|
||||
(defun prot-minibuffer--my-method-p (input-method)
|
||||
"Return non-nil if INPUT-METHOD is a member of `prot-minibuffer-used-input-methods'."
|
||||
(member input-method prot-minibuffer-used-input-methods))
|
||||
|
||||
(defun prot-minibuffer-input-method-sort (input-methods)
|
||||
"Sort INPUT-METHODS into `prot-minibuffer-used-input-methods' and the rest."
|
||||
(setq input-methods (prot-minibuffer--set-default-sort input-methods))
|
||||
(nconc (seq-filter #'prot-minibuffer--my-method-p input-methods)
|
||||
(seq-remove #'prot-minibuffer--my-method-p input-methods)))
|
||||
|
||||
(defun prot-minibuffer-input-method-group (input-method transform)
|
||||
"Group INPUT-METHOD into `prot-minibuffer-used-input-methods' and the rest.
|
||||
Do it when TRANSFORM is non-nil, else return INPUT-METHOD."
|
||||
(cond
|
||||
(transform input-method)
|
||||
((prot-minibuffer--my-method-p input-method) "My input methods")
|
||||
(t "Other input methods")))
|
||||
|
||||
;; NOTE 2025-12-19: Maybe there is a better way, but this is okay to start with.
|
||||
(defun prot-minibuffer-library-annotate (library)
|
||||
"Return the group documentation of LIBRARY."
|
||||
(when-let* ((group-documentation (get (intern-soft library) 'group-documentation)))
|
||||
(prot-minibuffer--propertize-suffix-with-space group-documentation)))
|
||||
|
||||
(defun prot-minibuffer-command-annotate (command)
|
||||
"Annotate COMMAND with its key binding and shortened documentation string."
|
||||
(let* ((symbol (intern-soft command))
|
||||
(key (if-let* ((binding (where-is-internal symbol overriding-local-map t))
|
||||
(description (key-description binding))
|
||||
(_ (and binding (not (stringp binding)))))
|
||||
(format " %s " (propertize description 'face 'help-key-binding))
|
||||
""))
|
||||
(doc (if-let* ((doc (condition-case nil
|
||||
(documentation symbol)
|
||||
(error nil)))
|
||||
(first-line (substring doc 0 (string-search "\n" doc))))
|
||||
(propertize first-line 'face 'completions-annotations)
|
||||
"")))
|
||||
(format "%s%s" key (prot-minibuffer--propertize-suffix-with-space doc))))
|
||||
|
||||
;;;; Completions
|
||||
|
||||
(defun prot-minibuffer-completions-tweak-style ()
|
||||
"Tweak the style of the Completions buffer."
|
||||
(setq-local mode-line-format nil)
|
||||
(setq-local cursor-in-non-selected-windows nil)
|
||||
(when (and completions-header-format
|
||||
(not (string-blank-p completions-header-format)))
|
||||
(setq-local display-line-numbers-offset -1))
|
||||
(display-line-numbers-mode 1))
|
||||
|
||||
(defun prot-minibuffer-quit-completions ()
|
||||
"Always quit the Completions window."
|
||||
(when-let* ((window (get-buffer-window "*Completions*")))
|
||||
(quit-window nil window)))
|
||||
|
||||
(defun prot-minibuffer-choose-completion-no-exit ()
|
||||
"Call `choose-completion' without exiting the minibuffer.
|
||||
Also see `prot-minibuffer-choose-completion-exit' and
|
||||
`prot-minibuffer-choose-completion-dwim'."
|
||||
(interactive)
|
||||
(if minibuffer-visible-completions
|
||||
(minibuffer-choose-completion :no-exit :no-quit)
|
||||
(choose-completion nil :no-exit :no-quit)
|
||||
(switch-to-minibuffer)))
|
||||
|
||||
(defun prot-minibuffer-choose-completion-exit ()
|
||||
"Call `choose-completion' and exit the minibuffer.
|
||||
Also see `prot-minibuffer-choose-completion-no-exit' and
|
||||
`prot-minibuffer-choose-completion-dwim'."
|
||||
(interactive)
|
||||
(if minibuffer-visible-completions
|
||||
(or (minibuffer-choose-completion-or-exit) (exit-minibuffer))
|
||||
(choose-completion nil :no-exit)
|
||||
(exit-minibuffer)))
|
||||
|
||||
(defun prot-minibuffer-crm-p ()
|
||||
"Return non-nil if `completing-read-multiple' is in use."
|
||||
(when-let* ((_ (featurep 'crm))
|
||||
(window (active-minibuffer-window))
|
||||
(buffer (window-buffer window)))
|
||||
(buffer-local-value 'crm-completion-table buffer)))
|
||||
|
||||
(defun prot-minibuffer-empty-p ()
|
||||
"Return non-nil if the minibuffer is empty."
|
||||
(eq (minibuffer-prompt-end) (point-max)))
|
||||
|
||||
(defun prot-minibuffer-choose-completion-dwim ()
|
||||
"Call `choose-completion' that exits only on a unique match.
|
||||
If the match is not unique, then complete up to the largest common
|
||||
prefix or, anyhow, continue with the completion (e.g. in `find-file'
|
||||
switch into the directory and then show the files therein).
|
||||
|
||||
Also see `prot-minibuffer-choose-completion-no-exit' and
|
||||
`prot-minibuffer-choose-completion-exit'."
|
||||
(interactive)
|
||||
(if (prot-minibuffer-crm-p)
|
||||
(if (prot-minibuffer-empty-p)
|
||||
(prot-minibuffer-choose-completion-no-exit)
|
||||
(prot-minibuffer-choose-completion-exit))
|
||||
(choose-completion nil :no-exit :no-quit)
|
||||
(switch-to-minibuffer)
|
||||
(minibuffer-completion-help)
|
||||
(unless (get-buffer-window "*Completions*")
|
||||
(exit-minibuffer))))
|
||||
|
||||
(define-advice minibuffer-completion-help (:around (&rest args) prot)
|
||||
"Make `minibuffer-completion-help' display *Completions* in a side window.
|
||||
Make the window be at slot 0, such that the *Help* buffer produced by
|
||||
`prot-minibuffer-completions-describe-at-point' is to its right."
|
||||
(let ((display-buffer-overriding-action
|
||||
`((display-buffer-reuse-mode-window display-buffer-in-side-window)
|
||||
(mode . completion-list-mode)
|
||||
(side . bottom)
|
||||
(slot . 0))))
|
||||
(apply args)))
|
||||
|
||||
(defun prot-minibuffer-completions-describe-at-point (symbol)
|
||||
"Describe SYMBOL at point inside the *Completions* buffer.
|
||||
Place the *Help* buffer in a side window, situated to the right of the
|
||||
*Completions* buffer. Make the window have the `prot-minibuffer-help'
|
||||
property, such that it can be found by `prot-minibuffer-completions-close-help'."
|
||||
(interactive
|
||||
(list
|
||||
(intern-soft
|
||||
(if minibuffer-visible-completions
|
||||
(completion--selected-candidate)
|
||||
(thing-at-point 'symbol)))))
|
||||
(when symbol
|
||||
(let ((help-window-select nil)
|
||||
(display-buffer-overriding-action
|
||||
`((display-buffer-reuse-mode-window display-buffer-in-side-window)
|
||||
(mode . help-mode)
|
||||
(side . bottom)
|
||||
(slot . 1)
|
||||
(window-height . fit-window-to-buffer)
|
||||
(window-parameters . ((prot-minibuffer-help . t))))))
|
||||
(describe-symbol symbol))))
|
||||
|
||||
(defun prot-minibuffer-completions-close-help ()
|
||||
"Close the window that has a `prot-minibuffer-help' parameter."
|
||||
(when-let* ((help (seq-find
|
||||
(lambda (window)
|
||||
(window-parameter window 'prot-minibuffer-help))
|
||||
(window-list))))
|
||||
(delete-window help)))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-minibuffer-completions-mode
|
||||
"Tweak the interface of the minibuffer and the *Completions*."
|
||||
:global t
|
||||
(if prot-minibuffer-completions-mode
|
||||
(progn
|
||||
(setq completion-show-help nil)
|
||||
(setq completion-show-inline-help nil)
|
||||
(setq completions-detailed t)
|
||||
(setq completions-format 'one-column)
|
||||
(setq completions-header-format "")
|
||||
(setq completions-max-height 12)
|
||||
(setq completions-sort 'historical)
|
||||
(setq completion-auto-help t)
|
||||
(setq completion-auto-select nil)
|
||||
(setq minibuffer-visible-completions t)
|
||||
(setq completion-eager-display 'auto)
|
||||
(setq completion-eager-update 'auto)
|
||||
(add-hook 'completion-list-mode-hook #'prot-minibuffer-completions-tweak-style)
|
||||
(add-hook 'minibuffer-exit-hook #'prot-minibuffer-quit-completions)
|
||||
(add-hook 'minibuffer-exit-hook #'prot-minibuffer-completions-close-help))
|
||||
(setq completion-show-help t)
|
||||
(setq completion-show-inline-help nil)
|
||||
(setq completions-detailed nil)
|
||||
(setq completions-format 'horizontal)
|
||||
(setq completions-header-format (propertize "%s possible completions:\n" 'face 'shadow))
|
||||
(setq completions-max-height nil)
|
||||
(setq completions-sort 'alphabetical)
|
||||
(setq completion-auto-help t)
|
||||
(setq completion-auto-select nil)
|
||||
(setq minibuffer-visible-completions nil)
|
||||
(setq completion-eager-display 'auto)
|
||||
(setq completion-eager-update 'auto)
|
||||
(remove-hook 'completion-list-mode-hook #'prot-minibuffer-completions-tweak-style)
|
||||
(remove-hook 'minibuffer-exit-hook #'prot-minibuffer-quit-completions)
|
||||
(remove-hook 'minibuffer-exit-hook #'prot-minibuffer-completions-close-help)))
|
||||
|
||||
(provide 'prot-minibuffer)
|
||||
;;; prot-minibuffer.el ends here
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-modeline.el --- Code for my custom mode line -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -23,15 +23,11 @@
|
|||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'prot-common)
|
||||
(require 'prot-icons)
|
||||
|
||||
(defgroup prot-modeline nil
|
||||
"Custom modeline that is stylistically close to the default."
|
||||
|
|
@ -53,6 +49,11 @@ Modify this face to, for example, add a :box attribute to all
|
|||
relevant indicators (combines nicely with my `spacious-padding'
|
||||
package).")
|
||||
|
||||
(defface prot-modeline-indicator-small
|
||||
'((t :height 0.8))
|
||||
"Face for modeline indicators (e.g. see my `notmuch-indicator')."
|
||||
:group 'prot-modeline-faces)
|
||||
|
||||
(defface prot-modeline-indicator-red
|
||||
'((default :inherit bold)
|
||||
(((class color) (min-colors 88) (background light))
|
||||
|
|
@ -174,7 +175,7 @@ package).")
|
|||
:group 'prot-modeline-faces)
|
||||
|
||||
(defface prot-modeline-indicator-gray
|
||||
'((t :inherit shadow))
|
||||
'((t :inherit (bold shadow)))
|
||||
"Face for modeline indicators (e.g. see my `notmuch-indicator')."
|
||||
:group 'prot-modeline-faces)
|
||||
|
||||
|
|
@ -192,11 +193,14 @@ package).")
|
|||
|
||||
(defun prot-modeline--string-truncate-p (str)
|
||||
"Return non-nil if STR should be truncated."
|
||||
(if (string-empty-p str)
|
||||
str
|
||||
(and (prot-common-window-narrow-p)
|
||||
(cond
|
||||
((or (not (stringp str))
|
||||
(string-empty-p str)
|
||||
(string-blank-p str))
|
||||
nil)
|
||||
((and (prot-common-window-narrow-p)
|
||||
(> (length str) prot-modeline-string-truncate-length)
|
||||
(not (one-window-p :no-minibuffer)))))
|
||||
(not (one-window-p :no-minibuffer))))))
|
||||
|
||||
(defun prot-modeline--truncate-p ()
|
||||
"Return non-nil if truncation should happen.
|
||||
|
|
@ -271,15 +275,16 @@ Specific to the current window's mode line.")
|
|||
'(:eval
|
||||
(when (and (mode-line-window-selected-p)
|
||||
(buffer-narrowed-p)
|
||||
(not (derived-mode-p 'Info-mode 'help-mode 'special-mode 'message-mode)))
|
||||
(not (memq major-mode '(Info-mode help-mode message-mode special-mode))))
|
||||
(propertize " Narrow " 'face 'prot-modeline-indicator-cyan-bg)))
|
||||
"Mode line construct to report the multilingual environment.")
|
||||
"Mode line construct to report the narrowed state of the current buffer.")
|
||||
|
||||
;;;; Input method
|
||||
|
||||
(defvar-local prot-modeline-input-method
|
||||
'(:eval
|
||||
(when current-input-method-title
|
||||
(when (and (mode-line-window-selected-p)
|
||||
current-input-method-title)
|
||||
(propertize (format " %s " current-input-method-title)
|
||||
'face 'prot-modeline-indicator-green-bg
|
||||
'mouse-face 'mode-line-highlight)))
|
||||
|
|
@ -325,16 +330,9 @@ Specific to the current window's mode line.")
|
|||
(defun prot-modeline--buffer-name ()
|
||||
"Return `buffer-name', truncating it if necessary.
|
||||
See `prot-modeline-string-cut-middle'."
|
||||
(when-let ((name (buffer-name)))
|
||||
(when-let* ((name (buffer-name)))
|
||||
(prot-modeline-string-cut-middle name)))
|
||||
|
||||
(defun prot-modeline-buffer-name ()
|
||||
"Return buffer name, with read-only indicator if relevant."
|
||||
(let ((name (prot-modeline--buffer-name)))
|
||||
(if buffer-read-only
|
||||
(format "%s %s" (char-to-string #xE0A2) name)
|
||||
name)))
|
||||
|
||||
(defun prot-modeline-buffer-name-help-echo ()
|
||||
"Return `help-echo' value for `prot-modeline-buffer-identification'."
|
||||
(concat
|
||||
|
|
@ -346,25 +344,27 @@ See `prot-modeline-string-cut-middle'."
|
|||
'face 'font-lock-doc-face)))
|
||||
|
||||
(defvar-local prot-modeline-buffer-identification
|
||||
'(:eval
|
||||
(propertize (prot-modeline-buffer-name)
|
||||
'face (prot-modeline-buffer-identification-face)
|
||||
'mouse-face 'mode-line-highlight
|
||||
'help-echo (prot-modeline-buffer-name-help-echo)))
|
||||
'(:eval
|
||||
(let* ((name (propertize (prot-modeline--buffer-name)
|
||||
'face (prot-modeline-buffer-identification-face)
|
||||
'mouse-face 'mode-line-highlight
|
||||
'help-echo (prot-modeline-buffer-name-help-echo)))
|
||||
(read-only-icon (prot-icons-get-icon
|
||||
'read-only
|
||||
(unless (mode-line-window-selected-p)
|
||||
'prot-modeline-indicator-gray))))
|
||||
(if buffer-read-only
|
||||
(format "%s %s" read-only-icon name)
|
||||
name)))
|
||||
"Mode line construct for identifying the buffer being displayed.
|
||||
Propertize the current buffer with the `mode-line-buffer-id'
|
||||
face. Let other buffers have no face.")
|
||||
|
||||
;;;; Major mode
|
||||
|
||||
(defun prot-modeline-major-mode-indicator ()
|
||||
"Return appropriate propertized mode line indicator for the major mode."
|
||||
(let ((indicator (cond
|
||||
((derived-mode-p 'text-mode) "§")
|
||||
((derived-mode-p 'prog-mode) "λ")
|
||||
((derived-mode-p 'comint-mode) ">_")
|
||||
(t "◦"))))
|
||||
(propertize indicator 'face 'shadow)))
|
||||
(defun prot-modeline-major-mode-icon ()
|
||||
"Return icon for the major mode."
|
||||
(prot-icons-get-icon major-mode 'prot-modeline-indicator-gray))
|
||||
|
||||
(defun prot-modeline-major-mode-name ()
|
||||
"Return capitalized `major-mode' without the -mode suffix."
|
||||
|
|
@ -372,7 +372,7 @@ face. Let other buffers have no face.")
|
|||
|
||||
(defun prot-modeline-major-mode-help-echo ()
|
||||
"Return `help-echo' value for `prot-modeline-major-mode'."
|
||||
(if-let ((parent (get major-mode 'derived-mode-parent)))
|
||||
(if-let* ((parent (get major-mode 'derived-mode-parent)))
|
||||
(format "Symbol: `%s'. Derived from: `%s'" major-mode parent)
|
||||
(format "Symbol: `%s'." major-mode)))
|
||||
|
||||
|
|
@ -381,7 +381,7 @@ face. Let other buffers have no face.")
|
|||
(propertize "%[" 'face 'prot-modeline-indicator-red)
|
||||
'(:eval
|
||||
(concat
|
||||
(prot-modeline-major-mode-indicator)
|
||||
(prot-modeline-major-mode-icon)
|
||||
" "
|
||||
(propertize
|
||||
(prot-modeline-string-abbreviate-but-last
|
||||
|
|
@ -402,31 +402,11 @@ face. Let other buffers have no face.")
|
|||
|
||||
(defun prot-modeline--vc-branch-name (file backend)
|
||||
"Return capitalized VC branch name for FILE with BACKEND."
|
||||
(when-let ((rev (vc-working-revision file backend))
|
||||
(branch (or (vc-git--symbolic-ref file)
|
||||
(substring rev 0 7))))
|
||||
(when-let* ((rev (vc-working-revision file backend))
|
||||
(branch (or (vc-git--symbolic-ref file)
|
||||
(substring rev 0 7))))
|
||||
(capitalize branch)))
|
||||
|
||||
;; NOTE 2023-07-27: This is a good idea, but it hardcodes Git, whereas
|
||||
;; I want a generic VC method. Granted, I only use Git but I still
|
||||
;; want it to work as a VC extension.
|
||||
|
||||
;; (defun prot-modeline-diffstat (file)
|
||||
;; "Return shortened Git diff numstat for FILE."
|
||||
;; (when-let* ((output (shell-command-to-string (format "git diff --numstat %s" file)))
|
||||
;; (stats (split-string output "[\s\t]" :omit-nulls "[\s\f\t\n\r\v]+"))
|
||||
;; (added (nth 0 stats))
|
||||
;; (deleted (nth 1 stats)))
|
||||
;; (cond
|
||||
;; ((and (equal added "0") (equal deleted "0"))
|
||||
;; "")
|
||||
;; ((and (not (equal added "0")) (equal deleted "0"))
|
||||
;; (propertize (format "+%s" added) 'face 'shadow))
|
||||
;; ((and (equal added "0") (not (equal deleted "0")))
|
||||
;; (propertize (format "-%s" deleted) 'face 'shadow))
|
||||
;; (t
|
||||
;; (propertize (format "+%s -%s" added deleted) 'face 'shadow)))))
|
||||
|
||||
(declare-function vc-git-working-revision "vc-git" (file))
|
||||
|
||||
(defvar prot-modeline-vc-map
|
||||
|
|
@ -434,7 +414,7 @@ face. Let other buffers have no face.")
|
|||
(define-key map [mode-line down-mouse-1] 'vc-diff)
|
||||
(define-key map [mode-line down-mouse-3] 'vc-root-diff)
|
||||
map)
|
||||
"Keymap to display on VC indicator.")
|
||||
"Keymap for the `prot-modeline-vc-branch'.")
|
||||
|
||||
(defun prot-modeline--vc-help-echo (file)
|
||||
"Return `help-echo' message for FILE tracked by VC."
|
||||
|
|
@ -444,17 +424,14 @@ face. Let other buffers have no face.")
|
|||
(defun prot-modeline--vc-text (file branch &optional face)
|
||||
"Prepare text for Git controlled FILE, given BRANCH.
|
||||
With optional FACE, use it to propertize the BRANCH."
|
||||
(concat
|
||||
(propertize (char-to-string #xE0A0) 'face 'shadow)
|
||||
" "
|
||||
(propertize branch
|
||||
'face face
|
||||
'mouse-face 'mode-line-highlight
|
||||
'help-echo (prot-modeline--vc-help-echo file)
|
||||
'local-map prot-modeline-vc-map)
|
||||
;; " "
|
||||
;; (prot-modeline-diffstat file)
|
||||
))
|
||||
(format "%s %s "
|
||||
(prot-icons-get-icon 'git 'prot-modeline-indicator-gray)
|
||||
;; (propertize (char-to-string #xE0A0) 'face 'prot-modeline-indicator-gray)
|
||||
(propertize branch
|
||||
'face face
|
||||
'mouse-face 'mode-line-highlight
|
||||
'help-echo (prot-modeline--vc-help-echo file)
|
||||
'local-map prot-modeline-vc-map)))
|
||||
|
||||
(defun prot-modeline--vc-details (file branch &optional face)
|
||||
"Return Git BRANCH details for FILE, truncating it if necessary.
|
||||
|
|
@ -475,17 +452,18 @@ than `split-width-threshold'."
|
|||
|
||||
(defun prot-modeline--vc-get-face (key)
|
||||
"Get face from KEY in `prot-modeline--vc-faces'."
|
||||
(alist-get key prot-modeline--vc-faces 'up-to-date))
|
||||
(alist-get key prot-modeline--vc-faces 'vc-up-to-date-state))
|
||||
|
||||
(defun prot-modeline--vc-face (file backend)
|
||||
"Return VC state face for FILE with BACKEND."
|
||||
(prot-modeline--vc-get-face (vc-state file backend)))
|
||||
(when-let* ((key (vc-state file backend)))
|
||||
(prot-modeline--vc-get-face key)))
|
||||
|
||||
(defvar-local prot-modeline-vc-branch
|
||||
'(:eval
|
||||
(when-let* (((mode-line-window-selected-p))
|
||||
(file (buffer-file-name))
|
||||
(backend (vc-backend file))
|
||||
(file (or buffer-file-name default-directory))
|
||||
(backend (or (vc-backend file) 'Git))
|
||||
;; ((vc-git-registered file))
|
||||
(branch (prot-modeline--vc-branch-name file backend))
|
||||
(face (prot-modeline--vc-face file backend)))
|
||||
|
|
@ -519,18 +497,18 @@ TYPE is usually keyword `:error', `:warning' or `:note'."
|
|||
(defmacro prot-modeline-flymake-type (type indicator &optional face)
|
||||
"Return function that handles Flymake TYPE with stylistic INDICATOR and FACE."
|
||||
`(defun ,(intern (format "prot-modeline-flymake-%s" type)) ()
|
||||
(when-let ((count (prot-modeline-flymake-counter
|
||||
,(intern (format ":%s" type)))))
|
||||
(when-let* ((count (prot-modeline-flymake-counter
|
||||
,(intern (format ":%s" type)))))
|
||||
(concat
|
||||
(propertize ,indicator 'face 'shadow)
|
||||
(propertize ,indicator 'face 'prot-modeline-indicator-gray)
|
||||
(propertize count
|
||||
'face ',(or face type)
|
||||
'mouse-face 'mode-line-highlight
|
||||
;; FIXME 2023-07-03: Clicking on the text with
|
||||
;; this buffer and a single warning present, the
|
||||
;; diagnostics take up the entire frame. Why?
|
||||
'local-map prot-modeline-flymake-map
|
||||
'help-echo "mouse-1: buffer diagnostics\nmouse-3: project diagnostics")))))
|
||||
'mouse-face 'mode-line-highlight
|
||||
;; FIXME 2023-07-03: Clicking on the text with
|
||||
;; this buffer and a single warning present, the
|
||||
;; diagnostics take up the entire frame. Why?
|
||||
'local-map prot-modeline-flymake-map
|
||||
'help-echo "mouse-1: buffer diagnostics\nmouse-3: project diagnostics")))))
|
||||
|
||||
(prot-modeline-flymake-type error "☣")
|
||||
(prot-modeline-flymake-type warning "!")
|
||||
|
|
@ -561,6 +539,52 @@ Specific to the current window's mode line.")
|
|||
"Mode line construct displaying Eglot information.
|
||||
Specific to the current window's mode line.")
|
||||
|
||||
;;;; Frame name
|
||||
|
||||
(defcustom prot-modeline-show-frame-name nil
|
||||
"When non-nil, display the current frame name."
|
||||
:type 'boolean)
|
||||
|
||||
(defvar prot-modeline-frame-name-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map [mode-line down-mouse-1] 'buffer-menu)
|
||||
map)
|
||||
"Keymap for the `prot-modeline-frame-name'.")
|
||||
|
||||
(defvar-local prot-modeline-frame-name
|
||||
'(prot-modeline-show-frame-name
|
||||
(" "
|
||||
(:eval
|
||||
(when-let* ((_ (mode-line-window-selected-p))
|
||||
(current-frame (selected-frame))
|
||||
(_ (frame-live-p current-frame))
|
||||
(parameters (frame-parameters))
|
||||
(name (capitalize (alist-get 'name parameters))))
|
||||
(format "%s %s "
|
||||
(prot-icons-get-icon 'frame 'prot-modeline-indicator-gray)
|
||||
(propertize name
|
||||
'mouse-face 'mode-line-highlight
|
||||
'help-echo (format "Current frame name\nmouse-1: `buffer-menu'")
|
||||
'local-map prot-modeline-frame-name-map))))))
|
||||
"Mode line construct to display the current frame name.")
|
||||
|
||||
;;;; `which-function-mode' indicator
|
||||
|
||||
(defvar-local prot-modeline-which-function-indicator
|
||||
'(which-function-mode
|
||||
(:eval
|
||||
(when (or (provided-mode-derived-p major-mode 'text-mode)
|
||||
(provided-mode-derived-p major-mode 'prog-mode))
|
||||
(let ((string (or (gethash (selected-window) which-func-table) which-func-unknown)))
|
||||
(propertize string
|
||||
'face '(prot-modeline-indicator-small which-func)
|
||||
'mouse-face 'mode-line-highlight
|
||||
'help-echo (format "Current definition: `%s'" string))))))
|
||||
"The equivalent of `which-func-format'.")
|
||||
|
||||
(with-eval-after-load 'which-func
|
||||
(setq mode-line-misc-info (delete (assq 'which-function-mode mode-line-misc-info) mode-line-misc-info)))
|
||||
|
||||
;;;; Miscellaneous
|
||||
|
||||
(defvar-local prot-modeline-notmuch-indicator
|
||||
|
|
@ -593,7 +617,8 @@ Specific to the current window's mode line.")
|
|||
prot-modeline-vc-branch
|
||||
prot-modeline-flymake
|
||||
prot-modeline-eglot
|
||||
;; prot-modeline-align-right
|
||||
prot-modeline-frame-name
|
||||
prot-modeline-which-function-indicator
|
||||
prot-modeline-notmuch-indicator
|
||||
prot-modeline-misc-info))
|
||||
(put construct 'risky-local-variable t))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-notmuch.el --- Tweaks for my notmuch.el configurations -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2021-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my tweaks for notmuch.el that are meant for use in my
|
||||
;; Emacs setup: https://protesilaos.com/emacs/dotemacs.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-orderless.el --- Extensions for Orderless -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2020-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2020-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -52,8 +52,8 @@
|
|||
|
||||
(defun prot-orderless-beg-or-end (word _index _total)
|
||||
"Expand WORD~ to \\(^WORD\\|WORD$\\)."
|
||||
(when-let (((string-suffix-p "~" word))
|
||||
(word (substring word 0 -1)))
|
||||
(when-let* (((string-suffix-p "~" word))
|
||||
(word (substring word 0 -1)))
|
||||
`(orderless-regexp . ,(format "\\(^%s\\|%s$\\)" word word))))
|
||||
|
||||
(provide 'prot-orderless)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-org.el --- Tweaks for my org-mode configurations -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2021-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my tweaks for Org that are meant for use in my
|
||||
;; Emacs setup: https://protesilaos.com/emacs/dotemacs.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -43,83 +38,6 @@
|
|||
|
||||
;;;; org-capture
|
||||
|
||||
(defvar prot-org--capture-coach-person-history nil)
|
||||
|
||||
(declare-function message-fetch-field "message" (header &optional first))
|
||||
(declare-function notmuch-show-get-header "notmuch-show")
|
||||
|
||||
(defun prot-org--capture-coach-person-message-from ()
|
||||
"Return default value for `prot-org--capture-coach-person-prompt'."
|
||||
(when-let ((from (cond
|
||||
((derived-mode-p 'message-mode)
|
||||
(message-fetch-field "To"))
|
||||
((derived-mode-p 'notmuch-show-mode)
|
||||
(notmuch-show-get-header :From)))))
|
||||
(string-clean-whitespace (car (split-string from "<")))))
|
||||
|
||||
(defun prot-org--capture-coach-person-message-from-and-subject ()
|
||||
"Return default value for `prot-org--capture-coach-person-prompt'."
|
||||
(cond
|
||||
((derived-mode-p 'message-mode)
|
||||
(message-fetch-field "Subject"))
|
||||
((derived-mode-p 'notmuch-show-mode)
|
||||
(notmuch-show-get-header :Subject))))
|
||||
|
||||
(defun prot-org--capture-coach-person-prompt ()
|
||||
"Prompt for person for use in `prot-org-capture-coach'."
|
||||
(completing-read "Person to coach: "
|
||||
prot-org--capture-coach-person-history
|
||||
nil nil nil
|
||||
'prot-org--capture-coach-person-history
|
||||
(prot-org--capture-coach-person-message-from)))
|
||||
|
||||
(defvar prot-org--capture-coach-description-history nil)
|
||||
|
||||
(defun prot-org--capture-coach-description-prompt ()
|
||||
"Prompt for description in `prot-org-capture-coach'."
|
||||
(read-string "Description: "
|
||||
nil
|
||||
'prot-org--capture-coach-description-history
|
||||
(prot-org--capture-coach-person-message-from-and-subject)))
|
||||
|
||||
(defun prot-org--capture-coach-date-prompt-range ()
|
||||
"Prompt for Org date and return it as a +1h range.
|
||||
For use in `prot-org-capture-coach'."
|
||||
(let ((date (org-read-date :with-time)))
|
||||
;; We cannot use this here, unfortunately, as the Org agenda
|
||||
;; interprets it both as a deadline and an event with the date
|
||||
;; range.
|
||||
;;
|
||||
;; (format "DEADLINE: <%s>--<%s>\n" date
|
||||
(format "<%s>--<%s>\n" date
|
||||
(org-read-date
|
||||
:with-time nil "++1h" nil
|
||||
(org-encode-time (org-parse-time-string date))))))
|
||||
|
||||
(defun prot-org-capture-coach ()
|
||||
"Contents of an Org capture template for my coaching lessons."
|
||||
(concat "* COACH " (prot-org--capture-coach-person-prompt) " "
|
||||
(prot-org--capture-coach-description-prompt) " :lesson:\n"
|
||||
;; See comment above
|
||||
;; (prot-org--capture-coach-date-prompt-range)
|
||||
"DEADLINE: %^T\n"
|
||||
":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":APPT_WARNTIME: 20\n"
|
||||
":END:\n\n"
|
||||
"%a%?"))
|
||||
|
||||
(defun prot-org-capture-coach-clock ()
|
||||
"Contents of an Org capture for my clocked coaching services."
|
||||
(concat "* COACH " (prot-org--capture-coach-person-prompt) " "
|
||||
(prot-org--capture-coach-description-prompt) " :service:\n"
|
||||
;; See comment above
|
||||
;; (prot-org--capture-coach-date-prompt-range)
|
||||
":PROPERTIES:\n"
|
||||
":CAPTURED: %U\n"
|
||||
":END:\n\n"
|
||||
"%a%?"))
|
||||
|
||||
(declare-function cl-letf "cl-lib")
|
||||
|
||||
;; Adapted from source: <https://stackoverflow.com/a/54251825>.
|
||||
|
|
@ -129,6 +47,7 @@ For use in `prot-org-capture-coach'."
|
|||
;; we were missing the `&rest':
|
||||
;; <https://github.com/org-roam/org-roam/issues/2142#issuecomment-1100718373>.
|
||||
(defun prot-org--capture-no-delete-windows (&rest args)
|
||||
"Apply ARGS while ignoring `delete-other-windows'."
|
||||
(cl-letf (((symbol-function 'delete-other-windows) 'ignore))
|
||||
(apply args)))
|
||||
|
||||
|
|
@ -136,6 +55,228 @@ For use in `prot-org-capture-coach'."
|
|||
(advice-add 'org-capture-place-template :around 'prot-org--capture-no-delete-windows)
|
||||
(advice-add 'org-add-log-note :around 'prot-org--capture-no-delete-windows)
|
||||
|
||||
;;;;; Custom function to select a project to add to
|
||||
|
||||
(defun prot-org--get-outline (&optional file)
|
||||
"Return `outline-regexp' headings and line numbers of current file or FILE."
|
||||
(with-current-buffer (find-file-noselect file)
|
||||
(let ((outline-regexp (format "^\\(?:%s\\)" (or (bound-and-true-p outline-regexp) "[*\^L]+")))
|
||||
candidates)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (if (bound-and-true-p outline-search-function)
|
||||
(funcall outline-search-function)
|
||||
(re-search-forward outline-regexp nil t))
|
||||
(push
|
||||
;; NOTE 2024-11-24: The -5 (minimum width) is a sufficiently high number to keep the
|
||||
;; alignment consistent in most cases. Larger files will simply shift the heading text
|
||||
;; in minibuffer, but this is not an issue anymore.
|
||||
(format "%-5s\t%s"
|
||||
(line-number-at-pos (point))
|
||||
(buffer-substring-no-properties (line-beginning-position) (line-end-position)))
|
||||
candidates)
|
||||
(goto-char (1+ (line-end-position)))))
|
||||
(if candidates
|
||||
(nreverse candidates)
|
||||
(user-error "No outline")))))
|
||||
|
||||
(defvar prot-org-outline-history nil
|
||||
"Minibuffer history for `prot-org-outline-prompt'.")
|
||||
|
||||
(defun prot-org-outline-prompt (&optional file)
|
||||
"Prompt for outline among headings retrieved by `prot-org--get-outline'.
|
||||
With optional FILE use the outline of it, otherwise use that of
|
||||
the current file."
|
||||
(let ((current-file (or file buffer-file-name))
|
||||
(default (car prot-org-outline-history)))
|
||||
(completing-read
|
||||
(format-prompt
|
||||
(format "Select heading inside `%s': "
|
||||
(propertize (file-name-nondirectory current-file) 'face 'error))
|
||||
default)
|
||||
(prot-common-completion-table-no-sort 'imenu (prot-org--get-outline current-file))
|
||||
nil :require-match nil 'prot-org-outline-history default)))
|
||||
|
||||
(defvar prot-org-file-history nil
|
||||
"Minibuffer history of `prot-org-file-prompt'.")
|
||||
|
||||
(defun prot-org--not-useful-p (file)
|
||||
"Return non-nil if FILE is not a useful Org file for `org-capture'."
|
||||
(or (string-match-p "\\.org_archive\\'" file)
|
||||
(backup-file-name-p file)
|
||||
(not (string-match-p "\\.org\\'" file))))
|
||||
|
||||
(defun prot-org-file-prompt ()
|
||||
"Select a file in the `org-directory'."
|
||||
(if-let* ((dir org-directory)
|
||||
(files (directory-files-recursively org-directory ".*" nil))
|
||||
(files (seq-remove #'prot-org--not-useful-p files)))
|
||||
(let ((default (car prot-org-file-history)))
|
||||
(completing-read
|
||||
(format-prompt "Select file" default)
|
||||
(prot-common-completion-table 'file files)
|
||||
nil :require-match nil 'prot-org-file-history default))
|
||||
(user-error "There are no files in the `org-directory'")))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-select-heading-in-file ()
|
||||
"Move to heading in Org file stored in `org-directory'.
|
||||
Prompt for file, then for heading inside of that file.
|
||||
|
||||
This function can also be used as part of an `org-capture' template to
|
||||
navigate to a file+heading and then capture something which is inserted
|
||||
there."
|
||||
(declare (interactive-only t))
|
||||
(interactive)
|
||||
(pcase-let* ((file (prot-org-file-prompt))
|
||||
(line-with-heading (prot-org-outline-prompt file))
|
||||
(`(,line ,text) (split-string line-with-heading "\t"))
|
||||
(line (string-to-number line)))
|
||||
;; NOTE 2024-11-24: `with-current-buffer' does not work with `org-capture'.
|
||||
(find-file file)
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- line))))
|
||||
|
||||
(defalias 'prot-org-goto-heading-in-file 'prot-org-select-heading-in-file
|
||||
"Alias for `prot-org-select-heading-in-file'.")
|
||||
|
||||
;;;; Org clock report
|
||||
|
||||
(defvar prot-org-clock--template-with-effort
|
||||
"#+BEGIN: clocktable :formula % :properties (\"Effort\") :timestamp t :sort (1 . ?a) :link t :scope nil :hidefiles t :maxlevel 8 :stepskip0 t
|
||||
#+END:"
|
||||
"Clock table with effort estimate column to use for custom clock reports.")
|
||||
|
||||
(defvar prot-org-clock--template-no-effort
|
||||
"#+BEGIN: clocktable :formula % :timestamp t :sort (1 . ?a) :link nil :scope nil :hidefiles t :maxlevel 8 :stepskip0 t
|
||||
#+END:"
|
||||
"Clock table to use for custom clock reports.")
|
||||
|
||||
(defvar prot-org-clock--ranges
|
||||
'( today yesterday thisweek lastweek thismonth
|
||||
lastmonth thisyear lastyear untilnow)
|
||||
"Time ranges of my interest for clock reports.")
|
||||
|
||||
(defvar prot-org-clock--report-range-history nil
|
||||
"Minibuffer history for `prot-org-clock--report-range-prompt'.")
|
||||
|
||||
(defun prot-org-clock--report-range-prompt ()
|
||||
"Prompt for a clock table range among `prot-org-clock--ranges'."
|
||||
(let ((default (car prot-org-clock--report-range-history)))
|
||||
(completing-read
|
||||
(format-prompt "Select a time range for the clock" default)
|
||||
prot-org-clock--ranges nil :require-match nil 'prot-org-clock--report-range-history
|
||||
default)))
|
||||
|
||||
(defun prot-org-clock--get-report (scope)
|
||||
"Produce clock report with current file SCOPE and return its buffer.
|
||||
SCOPE is a symbol of either `file' or `subtree'. If the former, then
|
||||
use the entire file's contents. Else use those of the current subtree."
|
||||
(let ((buffer (get-buffer-create "*prot-org-custom-clock-report*")))
|
||||
(save-restriction
|
||||
(unwind-protect
|
||||
(progn
|
||||
(pcase scope
|
||||
('file nil)
|
||||
('subtree (org-narrow-to-subtree))
|
||||
(_ (error "The scope `%s' is unknown" scope)))
|
||||
(let ((contents (buffer-substring (point-min) (point-max))))
|
||||
(with-current-buffer buffer
|
||||
(erase-buffer)
|
||||
(org-mode)
|
||||
(save-excursion
|
||||
(insert (format "%s\n\n" prot-org-clock--template-with-effort))
|
||||
(insert contents))
|
||||
(save-excursion
|
||||
(let ((range (prot-org-clock--report-range-prompt)))
|
||||
(goto-char (line-end-position))
|
||||
(insert (concat " :block " range))))
|
||||
(org-dblock-update))))
|
||||
(widen)))
|
||||
buffer))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-clock-report-current-subtree-or-file (&optional whole-buffer)
|
||||
"Produce a clock report in a new buffer for the subtree at point.
|
||||
With optional WHOLE-BUFFER as a non-nil value, operate on the entire file.
|
||||
When called interactively WHOLE-BUFFER is a prefix argument."
|
||||
(interactive "P")
|
||||
(when-let* ((buffer (prot-org-clock--get-report (if whole-buffer 'file 'subtree))))
|
||||
(pop-to-buffer buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-clock-select-heading-and-clock-report ()
|
||||
"Select a heading in a file and do a clock report for it in a new buffer."
|
||||
(interactive)
|
||||
(call-interactively 'prot-org-select-heading-in-file)
|
||||
(call-interactively 'prot-org-clock-report-current-subtree-or-file))
|
||||
|
||||
;;;;; Coaching-related Org custom clocking
|
||||
|
||||
;; TODO 2024-12-15: This sort of thing must exist in Org, but I did
|
||||
;; not find it.
|
||||
(defun prot-org--timestamp-to-time (string)
|
||||
"Return time object of STRING timestamp."
|
||||
(org-timestamp-to-time (org-timestamp-from-string string)))
|
||||
|
||||
(defun prot-org-coach--get-entries (todo-keyword string since)
|
||||
"Get Org entries matching TODO-KEYWORD followed by STRING in the heading.
|
||||
Limit entries to those whole deadline/scheduled is equal or greater to
|
||||
SINCE date.
|
||||
|
||||
Each entry is a plist of :heading, :contents, :started, :closed."
|
||||
(or (delq nil
|
||||
(org-map-entries
|
||||
(lambda ()
|
||||
(when-let* ((case-fold-search t)
|
||||
(started (prot-org--timestamp-to-time (or (org-entry-get nil "DEADLINE") (org-entry-get nil "SCHEDULED"))))
|
||||
(closed (prot-org--timestamp-to-time (org-entry-get nil "CLOSED")))
|
||||
((re-search-forward (format "\\<%s\\>.*\\<%s\\>" todo-keyword string) (line-end-position) t 1))
|
||||
((org-time-less-p since started)))
|
||||
(list
|
||||
:heading (org-get-heading :no-tags :no-todo :no-priority :no-comment)
|
||||
:contents (org-get-entry)
|
||||
:started started
|
||||
:closed closed)))))
|
||||
(user-error "No entries with heading matching `\\<%s\\>.*\\<%s\\>'" todo-keyword string)))
|
||||
|
||||
(defvar prot-org-coach--name-history nil
|
||||
"Minibuffer history of `prot-org-coach--name-prompt'.")
|
||||
|
||||
(defun prot-org-coach--name-prompt ()
|
||||
"Prompt for name of person."
|
||||
(let ((default (car prot-org-coach--name-history)))
|
||||
(read-string
|
||||
(format-prompt "Name of person" default)
|
||||
nil 'prot-org-coach--name-history default)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-coach-report (name since)
|
||||
"Produce clock report for coaching with person of NAME.
|
||||
SINCE is the date (of time 00:00) to count from until now."
|
||||
(interactive
|
||||
(list
|
||||
(prot-org-coach--name-prompt)
|
||||
(format "[%s]" (org-read-date))))
|
||||
(if-let* ((since-object (prot-org--timestamp-to-time since))
|
||||
(entries (prot-org-coach--get-entries "done" name since-object))
|
||||
(buffer (get-buffer-create "*prot-org-coach-entries*")))
|
||||
(with-current-buffer (pop-to-buffer buffer)
|
||||
(erase-buffer)
|
||||
(org-mode)
|
||||
(dolist (entry entries)
|
||||
(insert (format "* %s\n%s\n\n" (plist-get entry :heading) (plist-get entry :contents)))
|
||||
(org-clock-in nil (plist-get entry :started))
|
||||
(org-clock-out nil t (plist-get entry :closed)))
|
||||
(goto-char (point-min))
|
||||
(save-excursion
|
||||
(insert (format "%s\n\n" prot-org-clock--template-no-effort)))
|
||||
(save-excursion
|
||||
(goto-char (line-end-position))
|
||||
(insert (format " :tstart %S" since)))
|
||||
(org-dblock-update))
|
||||
(user-error "No entries for name `%s'" name)))
|
||||
|
||||
;;;; org-agenda
|
||||
|
||||
(declare-function calendar-day-name "calendar")
|
||||
|
|
@ -175,6 +316,17 @@ produces dates with a fixed length."
|
|||
|
||||
(defvar org-priority-highest)
|
||||
|
||||
(defun prot-org-agenda-include-priority-no-timestamp ()
|
||||
"Return nil if heading has a priority but no timestamp.
|
||||
Otherwise, return the buffer position from where the search should
|
||||
continue, per `org-agenda-skip-function'."
|
||||
(let ((point (point)))
|
||||
(if (and (eq (nth 3 (org-heading-components)) ?A)
|
||||
(not (org-get-deadline-time point))
|
||||
(not (org-get-scheduled-time point)))
|
||||
nil
|
||||
(line-beginning-position 2))))
|
||||
|
||||
(defvar prot-org-custom-daily-agenda
|
||||
;; NOTE 2021-12-08: Specifying a match like the following does not
|
||||
;; work.
|
||||
|
|
@ -185,10 +337,15 @@ produces dates with a fixed length."
|
|||
;; `org-agenda-skip-function'.
|
||||
`((tags-todo "*"
|
||||
((org-agenda-overriding-header "Important tasks without a date\n")
|
||||
(org-agenda-skip-function '(org-agenda-skip-if nil '(timestamp)))
|
||||
(org-agenda-skip-function
|
||||
`(org-agenda-skip-entry-if
|
||||
'notregexp ,(format "\\[#%s\\]" (char-to-string org-priority-highest))))
|
||||
;; NOTE 2024-10-31: Those used to work, but now the
|
||||
;; query for the timestamp is ignored. I thus wrote
|
||||
;; `prot-org-agenda-include-priority-no-timestamp'.
|
||||
;;
|
||||
;; (org-agenda-skip-function '(org-agenda-skip-subtree-if nil '(timestamp)))
|
||||
;; (org-agenda-skip-function
|
||||
;; `(org-agenda-skip-entry-if
|
||||
;; 'notregexp ,(format "\\[#%s\\]" (char-to-string org-priority-highest))))
|
||||
(org-agenda-skip-function #'prot-org-agenda-include-priority-no-timestamp)
|
||||
(org-agenda-block-separator nil)))
|
||||
(agenda "" ((org-agenda-overriding-header "\nPending scheduled tasks")
|
||||
(org-agenda-time-grid nil)
|
||||
|
|
@ -250,6 +407,29 @@ produces dates with a fixed length."
|
|||
(org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)))))
|
||||
"Custom agenda for use in `org-agenda-custom-commands'.")
|
||||
|
||||
(defun prot-org-custom-agenda-prot-asks-get-date ()
|
||||
"Return the timestamp of the current heading or nil.
|
||||
For use in `prot-org-custom-prot-asks-agenda'."
|
||||
(when-let* ((timestamp (or (org-entry-get nil "DEADLINE") (org-entry-get nil "SCHEDULED")))
|
||||
(time (prot-org--timestamp-to-time timestamp)))
|
||||
(format-time-string "%10A %e %10B %6R" time)))
|
||||
|
||||
(defvar prot-org-custom-prot-asks-agenda
|
||||
'((tags-todo "protasks"
|
||||
((org-agenda-overriding-header "Prot Asks\n")
|
||||
(org-agenda-prefix-format '((tags . "%(prot-org-custom-agenda-prot-asks-get-date) ")))
|
||||
(org-agenda-sorting-strategy '(deadline-up))
|
||||
(org-agenda-remove-tags t)
|
||||
(org-agenda-block-separator nil))))
|
||||
"Custom agenda for use in `org-agenda-custom-commands'.")
|
||||
|
||||
(defun prot-org-agenda-set-outline ()
|
||||
"Set `outline-regexp' for my Org agenda buffers."
|
||||
(when (derived-mode-p 'org-agenda-mode)
|
||||
(setq-local outline-regexp "^\\([\s\t]+\\)\\([A-Z]+ \\|[0-9]+:[0-9]+ \\)\\(Deadline\\|Scheduled\\)")))
|
||||
|
||||
(add-hook 'org-agenda-mode-hook #'prot-org-agenda-set-outline)
|
||||
|
||||
;;;;; agenda appointments
|
||||
|
||||
(defvar prot-org-agenda-after-edit-hook nil
|
||||
|
|
@ -325,11 +505,29 @@ If the entry already has a CUSTOM_ID, return it as-is, else
|
|||
create a new one."
|
||||
(let* ((pos (point))
|
||||
(id (org-entry-get pos "CUSTOM_ID")))
|
||||
(if (and id (stringp id) (string-match-p "\\S-" id))
|
||||
id
|
||||
(setq id (org-id-new "h"))
|
||||
(org-entry-put pos "CUSTOM_ID" id)
|
||||
id)))
|
||||
(or (and id (stringp id) (string-match-p "\\S-" id))
|
||||
(and (setq id (org-id-new "h")) (org-entry-put pos "CUSTOM_ID" id)))
|
||||
id))
|
||||
|
||||
(defun prot-org--heading-to-id ()
|
||||
"Convert current heading text to an ID for CUSTOM_ID purposes."
|
||||
(thread-last
|
||||
(org-get-heading :no-tags :no-todo :no-priority :no-comment)
|
||||
(replace-regexp-in-string "[][{}!@#$%^&*()+'\"?,.\|;:~`‘’“”/=]*" "")
|
||||
(replace-regexp-in-string "\s" "-")
|
||||
(string-trim)
|
||||
(downcase)
|
||||
(concat "h:")))
|
||||
|
||||
(defun prot-org--id-get-readable (&optional force)
|
||||
"Like `prot-org--id-get' but use the heading wording to create and ID.
|
||||
With optional FORCE, update the value even if one exists."
|
||||
(let* ((pos (point))
|
||||
(id (unless force (org-entry-get pos "CUSTOM_ID"))))
|
||||
(or (and id (stringp id) (string-match-p "\\S-" id))
|
||||
(and (setq id (prot-org--heading-to-id))
|
||||
(org-entry-put pos "CUSTOM_ID" id)))
|
||||
id))
|
||||
|
||||
(declare-function org-map-entries "org")
|
||||
|
||||
|
|
@ -337,14 +535,93 @@ create a new one."
|
|||
(defun prot-org-id-headlines ()
|
||||
"Add missing CUSTOM_ID to all headlines in current file."
|
||||
(interactive)
|
||||
(org-map-entries
|
||||
(lambda () (prot-org--id-get))))
|
||||
(org-map-entries (lambda () (prot-org--id-get))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-id-headline ()
|
||||
"Add missing CUSTOM_ID to headline at point."
|
||||
(defun prot-org-id-headlines-readable (&optional force)
|
||||
"Like `prot-org-id-headlines' but with readable IDs.
|
||||
A readable identifier is one derived from the text of the heading. In
|
||||
theory, this may not be unique.
|
||||
|
||||
With optional FORCE, update the value even if one exists."
|
||||
(interactive "P")
|
||||
(org-map-entries (lambda () (prot-org--id-get-readable force))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-id-headline (&optional readable)
|
||||
"Add missing CUSTOM_ID to headline at point.
|
||||
With optional prefix argument READABLE get a readable identifier derived
|
||||
from the heading text instead of a UUID."
|
||||
(interactive "P")
|
||||
(funcall (if readable 'prot-org--id-get-readable 'prot-org--id-get)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-get-dotemacs-link ()
|
||||
"Get URL to current heading in my dotemacs file."
|
||||
(declare (interactive-only t))
|
||||
(interactive)
|
||||
(prot-org--id-get))
|
||||
(if (and (derived-mode-p 'org-mode)
|
||||
(string-match-p "prot-emacs\\.org\\'" buffer-file-name))
|
||||
(if-let* ((id (org-entry-get (point) "CUSTOM_ID"))
|
||||
(url (concat "https://protesilaos.com/emacs/dotemacs#" id)))
|
||||
(progn
|
||||
(kill-new url)
|
||||
(message "Copied %s" (propertize url 'face 'success)))
|
||||
(error "No CUSTOM_ID for the current entry"))
|
||||
(user-error "You are not in the right file")))
|
||||
|
||||
;;;; Org and Diary integration
|
||||
|
||||
(defun prot-org-diary--get-file (ics-file)
|
||||
"Return diary file path that reuses the name of ICS-FILE.
|
||||
The path is relative to the `diary-file'."
|
||||
(unless (file-exists-p diary-file)
|
||||
(error "The `diary-file' does not exist"))
|
||||
(let* ((directory (file-name-directory diary-file))
|
||||
(ics-name (file-name-sans-extension (file-name-nondirectory ics-file)))
|
||||
(diary-name (format "%s%s-%s-diary.txt" directory (format-time-string "%Y-%m-%d") ics-name)))
|
||||
(when-let* ((buffer (get-file-buffer diary-name)))
|
||||
(with-current-buffer buffer
|
||||
(erase-buffer)))
|
||||
(when (file-exists-p diary-name)
|
||||
(delete-file diary-name))
|
||||
diary-name))
|
||||
|
||||
(defun prot-org-diary-include-file (diary-file-to-include)
|
||||
"Include DIARY-FILE-TO-INCLUDE in the `diary-file'."
|
||||
(unless (file-exists-p diary-file)
|
||||
(error "The `diary-file' does not exist"))
|
||||
(with-current-buffer (find-file-noselect diary-file)
|
||||
(let ((include-directive (format "#include %S" diary-file-to-include)))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward (format "^%s.*" include-directive) nil t)
|
||||
(delete-region (match-beginning 0) (match-end 0)))
|
||||
(goto-char (point-max))
|
||||
(insert "\n")
|
||||
(insert include-directive)
|
||||
(insert "\n")
|
||||
(save-buffer)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-org-diary-import-ics (ics-file diary-file-to-include)
|
||||
"Import ICS-FILE to DIARY-FILE."
|
||||
(interactive
|
||||
(let ((ics-file (read-file-name "Which ICS file? ")))
|
||||
(list
|
||||
ics-file
|
||||
(prot-org-diary--get-file ics-file))))
|
||||
(save-window-excursion
|
||||
(icalendar-import-file ics-file diary-file-to-include))
|
||||
(let ((diary-file-propertized (propertize diary-file-to-include 'face 'success))
|
||||
(main-diary-file-propertized (propertize diary-file 'face 'warning)))
|
||||
(when (yes-or-no-p (format
|
||||
"Imported `%s' as `%s'\nINCLUDE IT in `%s'?"
|
||||
(propertize ics-file 'face 'error)
|
||||
diary-file-propertized
|
||||
main-diary-file-propertized))
|
||||
(prot-org-diary-include-file diary-file-to-include)
|
||||
(message "Appended `%s' to `%s'" diary-file-propertized main-diary-file-propertized))))
|
||||
|
||||
(provide 'prot-org)
|
||||
;;; prot-org.el ends here
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-pair.el --- Insert character pair around symbol or region -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; Insert character pair around symbol or region using minibuffer
|
||||
;; completion.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -39,8 +34,8 @@
|
|||
:group 'editing)
|
||||
|
||||
(defcustom prot-pair-pairs
|
||||
'((?' :description "Single quotes" :pair (?' . ?'))
|
||||
(?\" :description "Double quotes" :pair (?\" . ?\"))
|
||||
'((?' :description "Single quotes" :pair ?')
|
||||
(?\" :description "Double quotes" :pair ?\")
|
||||
(?‘ :description "Single curly quotes" :pair (?‘ . ?’))
|
||||
(?“ :description "Double curly quotes" :pair (?“ . ?”))
|
||||
(?\> :description "Natural language quotes" :pair prot-pair-insert-natural-language-quotes)
|
||||
|
|
@ -48,20 +43,20 @@
|
|||
(?{ :description "Curly brackets" :pair (?{ . ?}))
|
||||
(?\[ :description "Square brackets" :pair (?\[ . ?\]))
|
||||
(?\< :description "Angled brackets" :pair (?\< . ?\>))
|
||||
(?@ :description "At signs" :pair (?@ . ?@))
|
||||
(?= :description "Equals signs" :pair (?= . ?=))
|
||||
(?+ :description "Plus signs" :pair (?+ . ?+))
|
||||
(?@ :description "At signs" :pair ?@)
|
||||
(?= :description "Equals signs" :pair ?=)
|
||||
(?+ :description "Plus signs" :pair ?+)
|
||||
(?` :description "Backticks" :pair prot-pair-insert-backticks)
|
||||
(?~ :description "Tildes" :pair (?~ . ?~))
|
||||
(?* :description "Asterisks" :pair (?* . ?*))
|
||||
(?/ :description "Forward slashes" :pair (?/ . ?/))
|
||||
(?_ :description "Underscores" :pair (?_ . ?_)))
|
||||
(?~ :description "Tildes" :pair ?~)
|
||||
(?* :description "Asterisks" :pair ?*)
|
||||
(?/ :description "Forward slashes" :pair ?/)
|
||||
(?_ :description "Underscores" :pair ?_))
|
||||
"Alist of pairs for use with `prot-pair-insert'.
|
||||
Each element in the list is a list whose `car' is a character and
|
||||
the `cdr' is a plist with a `:description' and `:pair' keys. The
|
||||
`:description' is a string used to describe the character/pair in
|
||||
interactive use, while `:pair' is a cons cell referencing the
|
||||
opening and closing characters.
|
||||
interactive use, while `:pair' is either a cons cell referencing the
|
||||
opening and closing characters or a single character.
|
||||
|
||||
The value of `:pair' can also be the unquoted symbol of a
|
||||
function. The function is called with no arguments and must
|
||||
|
|
@ -98,27 +93,29 @@ backtick and single quote, else two backticks."
|
|||
|
||||
(defun prot-pair--annotate (character)
|
||||
"Annotate CHARACTER with its description in `prot-pair-pairs'."
|
||||
(when-let ((char (if (characterp character) character (string-to-char character)))
|
||||
(plist (alist-get char prot-pair-pairs))
|
||||
(description (plist-get plist :description)))
|
||||
(when-let* ((char (if (characterp character) character (string-to-char character)))
|
||||
(plist (alist-get char prot-pair-pairs))
|
||||
(description (plist-get plist :description)))
|
||||
(format " %s" description)))
|
||||
|
||||
(defun prot-pair--get-pair (character)
|
||||
"Get the pair of corresponding to CHARACTER."
|
||||
(when-let ((char (if (characterp character) character (string-to-char character)))
|
||||
(plist (alist-get char prot-pair-pairs))
|
||||
(pair (plist-get plist :pair)))
|
||||
pair))
|
||||
"Get the pair corresponding to CHARACTER."
|
||||
(when-let* ((plist (alist-get character prot-pair-pairs))
|
||||
(pair-value (plist-get plist :pair)))
|
||||
(if (characterp pair-value)
|
||||
(cons pair-value pair-value)
|
||||
pair-value)))
|
||||
|
||||
(defun prot-pair--insert-prompt ()
|
||||
"Prompt for pair among `prot-pair-pairs'."
|
||||
(let ((default (car prot-pair--insert-history))
|
||||
(candidates (mapcar (lambda (char) (char-to-string (car char))) prot-pair-pairs))
|
||||
(completion-extra-properties `(:annotation-function ,#'prot-pair--annotate)))
|
||||
(completing-read
|
||||
(format-prompt "Select pair" default)
|
||||
candidates nil :require-match
|
||||
nil 'prot-pair--insert-history default)))
|
||||
(let* ((default (car prot-pair--insert-history))
|
||||
(candidates (mapcar (lambda (char) (char-to-string (car char))) prot-pair-pairs))
|
||||
(completion-extra-properties `(:annotation-function ,#'prot-pair--annotate))
|
||||
(choice (completing-read
|
||||
(format-prompt "Select pair" default)
|
||||
candidates nil :require-match
|
||||
nil 'prot-pair--insert-history default)))
|
||||
(string-to-char choice)))
|
||||
|
||||
(defun prot-pair--insert-bounds ()
|
||||
"Return boundaries of symbol at point or active region."
|
||||
|
|
@ -126,29 +123,44 @@ backtick and single quote, else two backticks."
|
|||
(cons (region-beginning) (region-end))
|
||||
(bounds-of-thing-at-point 'symbol)))
|
||||
|
||||
(defun prot-pair-insert-subr (character n)
|
||||
"Insert pair of CHARACTER as many as N times."
|
||||
(if-let* ((pair (prot-pair--get-pair character)))
|
||||
(let* ((bounds (prot-pair--insert-bounds))
|
||||
(beg (car bounds))
|
||||
(end (1+ (cdr bounds))) ; 1+ because we want the character after it
|
||||
(characters (if (functionp pair) (funcall pair) pair)))
|
||||
(dotimes (_ n)
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(insert (car characters))
|
||||
(goto-char end)
|
||||
(setq end (1+ end))
|
||||
(insert (cdr characters))))
|
||||
(goto-char (+ end (1- n))))
|
||||
(user-error "Cannot find character `%s' in `prot-pair-pairs'" (char-to-string character))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-pair-insert (pair n)
|
||||
"Insert N number of PAIR around object at point.
|
||||
PAIR is one among `prot-pair-pairs'. The object at point is
|
||||
either a symbol or the boundaries of the active region. N is a
|
||||
numeric prefix argument, defaulting to 1 if none is provided in
|
||||
interactive use."
|
||||
(defun prot-pair-insert (character n)
|
||||
"Insert CHARACTER pair an N number of times around object at point.
|
||||
CHARACTER is one among `prot-pair-pairs'. The object at point is either
|
||||
a symbol or the boundaries of the active region. N is a numeric prefix
|
||||
argument, defaulting to 1 if none is provided in interactive use."
|
||||
(interactive
|
||||
(list
|
||||
(prot-pair--get-pair (prot-pair--insert-prompt))
|
||||
(prot-pair--insert-prompt)
|
||||
(prefix-numeric-value current-prefix-arg)))
|
||||
(let* ((bounds (prot-pair--insert-bounds))
|
||||
(beg (car bounds))
|
||||
(end (1+ (cdr bounds))) ; 1+ because we want the character after it
|
||||
(characters (if (functionp pair) (funcall pair) pair)))
|
||||
(dotimes (_ n)
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(insert (car characters))
|
||||
(goto-char end)
|
||||
(setq end (1+ end))
|
||||
(insert (cdr characters))))
|
||||
(goto-char (+ end (1- n)))))
|
||||
(prot-pair-insert-subr character n))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-pair-insert-directly (character n)
|
||||
"Read CHARACTER, find it in `prot-pair-pairs', and insert it outright.
|
||||
Do not use minibuffer completion like `prot-pair-insert'."
|
||||
(interactive
|
||||
(list
|
||||
(read-char)
|
||||
(prefix-numeric-value current-prefix-arg)))
|
||||
(prot-pair-insert-subr character n))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-pair-delete ()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-prefix.el --- Prefix keymap for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -25,11 +25,6 @@
|
|||
;;; Commentary:
|
||||
;;
|
||||
;; Prefix keymap for my custom keymaps.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -185,9 +180,9 @@
|
|||
;; some do not. The former are those which define a prefix command
|
||||
;; (per `define-prefix-command'). This is a symbol that references
|
||||
;; the keymaps, thus making our binding an indirection: if we update
|
||||
;; the key map, we automatically get the new key bindings. Whereas
|
||||
;; the keymap, we automatically get the new key bindings. Whereas
|
||||
;; when we bind a key to the value of a variable, we have to update
|
||||
;; the key map and then the binding for changes to propagate.
|
||||
;; the keymap and then the binding for changes to propagate.
|
||||
(defvar-keymap prot-prefix-map
|
||||
:doc "Prefix keymap with multiple subkeymaps."
|
||||
:name "Prot Prefix"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-project.el --- Extensions for project.el -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2024 Protesilaos Stavrou
|
||||
;; Copyright (C) 2025-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -25,11 +25,6 @@
|
|||
;;; Commentary:
|
||||
;;
|
||||
;; Extensions for project.el.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -57,6 +52,9 @@ With optional COMMAND, run it in DIRECTORY."
|
|||
"Return a list of frame names."
|
||||
(mapcar #'car (make-frame-names-alist)))
|
||||
|
||||
(defvar prot-project-switch-hook nil
|
||||
"Normal hook called after `prot-project-switch'.")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-project-switch (directory)
|
||||
"Switch to project DIRECTORY.
|
||||
|
|
@ -67,15 +65,17 @@ the project in DIRECTORY using `project-dired'."
|
|||
(let ((name (file-name-nondirectory (directory-file-name directory))))
|
||||
(if (member name (prot-project--frame-names))
|
||||
(select-frame-by-name name)
|
||||
(prot-project--switch directory 'project-dired))))
|
||||
(prot-project--switch directory 'project-dired))
|
||||
(run-hooks 'prot-project-switch-hook)
|
||||
(setq this-command 'project-switch-project)))
|
||||
|
||||
;;;; Produce a VC root log for the project
|
||||
|
||||
(defun prot-project-rename-vc-root-log (&rest _)
|
||||
"Rename the buffer of `vc-print-root-log' to mention the project."
|
||||
(when-let ((root (vc-root-dir))
|
||||
((consp project--list))
|
||||
((member root (mapcar #'car project--list))))
|
||||
(when-let* ((root (vc-root-dir))
|
||||
((consp project--list))
|
||||
((member root (mapcar #'car project--list))))
|
||||
(rename-buffer (format "*vc-root-log: %s*" root))))
|
||||
|
||||
(advice-add #'vc-print-root-log :after #'prot-project-rename-vc-root-log)
|
||||
|
|
@ -98,7 +98,7 @@ If FRAME is nil, use the current frame."
|
|||
(tab-new)
|
||||
(find-file directory)
|
||||
(unwind-protect
|
||||
(prot-project--switch directory)
|
||||
(prot-project--switch directory 'project-dired)
|
||||
(tab-rename name)
|
||||
;; NOTE 2024-01-15 06:52 +0200: I am adding this because
|
||||
;; `tab-rename' is not persistent for some reason. Probably a bug...
|
||||
|
|
@ -119,7 +119,19 @@ Use this as an alternative to `project-switch-project'."
|
|||
(let ((name (file-name-nondirectory (directory-file-name directory))))
|
||||
(if (member name (prot-project-in-tab--get-tab-names))
|
||||
(tab-switch name)
|
||||
(prot-project-in-tab--create-tab directory name))))
|
||||
(prot-project-in-tab--create-tab directory name))
|
||||
(setq this-command 'project-switch-project)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-project-maybe-in-tab ()
|
||||
"Switch to project depending on `beframe-mode'.
|
||||
When the mode is enabled (the expected behaviour), use the command
|
||||
`prot-project-switch'. Otherwise, do `prot-project-in-tab'."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (bound-and-true-p beframe-mode)
|
||||
'prot-project-switch
|
||||
'prot-project-in-tab)))
|
||||
|
||||
;;;; Set up a project root
|
||||
|
||||
|
|
@ -135,7 +147,7 @@ is a string."
|
|||
(file-symbol (intern file)))
|
||||
`(progn
|
||||
(defun ,project-find-fn (dir)
|
||||
(when-let ((root (locate-dominating-file dir ,file)))
|
||||
(when-let* ((root (locate-dominating-file dir ,file)))
|
||||
(cons ',file-symbol root)))
|
||||
|
||||
(cl-defmethod project-root ((project (head ,file-symbol)))
|
||||
|
|
@ -146,5 +158,19 @@ is a string."
|
|||
|
||||
(add-hook ',(intern (format "%s-hook" mode)) #',major-mode-fn))))
|
||||
|
||||
;;;; Enable completion in the `compile' command
|
||||
|
||||
(defun prot-project-compilation-read-command (command)
|
||||
"Like `compilation-read-command' for COMMAND with completion against the `compile-history'."
|
||||
(if-let* ((history compile-history)
|
||||
(default (car history)))
|
||||
(completing-read "Compile command: " history nil nil nil 'compile-history default)
|
||||
(read-shell-command "Compile command: " command
|
||||
(if (equal (car compile-history) command)
|
||||
'(compile-history . 1)
|
||||
'compile-history))))
|
||||
|
||||
(advice-add #'compilation-read-command :override #'prot-project-compilation-read-command)
|
||||
|
||||
(provide 'prot-project)
|
||||
;;; prot-project.el ends here
|
||||
|
|
|
|||
155
emacs/.emacs.d/prot-lisp/prot-register.el
Normal file
155
emacs/.emacs.d/prot-lisp/prot-register.el
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
;;; prot-register.el --- Extensions for project.el -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2025-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or (at
|
||||
;; your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Extensions for register.el.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'register)
|
||||
|
||||
;;;; Define new register for file with point
|
||||
|
||||
(cl-defmethod register--type ((_regval vector)) 'vector)
|
||||
|
||||
(cl-defmethod register-val-describe ((val vector) _verbose)
|
||||
(if-let* ((position (aref val 2))
|
||||
(file (aref val 1)))
|
||||
(princ (format "%s at position %s" file position))
|
||||
(princ "Garbage data")))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-file-to-register (register)
|
||||
"Store current location of file's point in REGISTER."
|
||||
(interactive (list (register-read-with-preview "File with point to register: ")))
|
||||
(set-register register (vector 'file-with-point (buffer-file-name) (point))))
|
||||
|
||||
(defvar prot-simple-file-to-register-jump-hook nil
|
||||
"Normal hook called after jumping to a file register.
|
||||
See `prot-simple-file-to-register'.")
|
||||
|
||||
;;;###autoload
|
||||
(cl-defmethod register-val-jump-to ((val vector) delete)
|
||||
"Handle how to jump to a location register.
|
||||
This is like the default, but does not ask to visit a file: it does it
|
||||
outright."
|
||||
(cond
|
||||
((eq (aref val 0) 'file-with-point)
|
||||
(find-file (aref val 1))
|
||||
(goto-char (aref val 2))
|
||||
(run-hooks 'prot-simple-file-to-register-jump-hook))
|
||||
(t (cl-call-next-method val delete))))
|
||||
|
||||
;;;; Do-What-I-Mean commands
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-register-add-dwim (register &optional number)
|
||||
"Do-What-I-Mean with REGISTER.
|
||||
If the region is active, call `copy-to-register'.
|
||||
|
||||
If the optional prefix argument NUMBER is non-nil, then call
|
||||
`number-to-register'.
|
||||
|
||||
If there are more than two windows or there are more than one
|
||||
`tab-bar-mode' tabs, then do `frameset-to-register'.
|
||||
|
||||
Otherwise, use `prot-simple-file-to-register'.
|
||||
|
||||
Also see `prot-register-use-dwim'."
|
||||
(interactive
|
||||
(list
|
||||
(register-read-with-preview "Add register: ")
|
||||
(when current-prefix-arg
|
||||
(prefix-numeric-value current-prefix-arg))))
|
||||
(cond
|
||||
((when-let* ((_ (region-active-p))
|
||||
(beg (region-beginning))
|
||||
(end (region-end))
|
||||
(text (buffer-substring beg end))
|
||||
(_ (not (string-blank-p text))))
|
||||
(copy-to-register register beg end)
|
||||
(message "Copied `%s' to register `%c'" text register)))
|
||||
(number
|
||||
(number-to-register number register)
|
||||
(message "Copied number `%d' to register `%c'" number register))
|
||||
((or (length> (window-list) 2)
|
||||
(and (bound-and-true-p tab-bar-mode)
|
||||
(length> (tab-bar-tabs) 1)))
|
||||
(frameset-to-register register)
|
||||
(message "Copied current frameset to register `%c'" register))
|
||||
(t
|
||||
(prot-simple-file-to-register register)
|
||||
(message "Copied current point in file to register `%c'" register))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-register-use-dwim (register)
|
||||
"Use the REGISTER, jumping or inserting it, depending on its ocntents."
|
||||
(interactive (list (register-read-with-preview "Use register: ")))
|
||||
(let ((contents (get-register register)))
|
||||
(cond
|
||||
((register--jumpable-p contents)
|
||||
(jump-to-register register))
|
||||
((or (register--insertable-p contents)
|
||||
(numberp register))
|
||||
(insert-register register))
|
||||
(t
|
||||
(error "The register is unknown: %S" contents)))))
|
||||
|
||||
;;;; The `prot-register-persist-mode'
|
||||
|
||||
(defcustom prot-register-save-file (locate-user-emacs-file "prot-register.eld")
|
||||
"File to store registers to and retrieve from."
|
||||
:type 'file)
|
||||
|
||||
(defun prot-register-store ()
|
||||
"Write `register-alist' to `prot-register-save-file'."
|
||||
(with-temp-file prot-register-save-file
|
||||
(insert ";; Auto-generated file: DO NOT EDIT -*- mode: emacs-lisp -*-\n")
|
||||
(pp register-alist (current-buffer))))
|
||||
|
||||
(defun prot-register-load ()
|
||||
"Read `prot-register-save-file' and return its contents."
|
||||
(with-temp-buffer
|
||||
(when (file-exists-p prot-register-save-file)
|
||||
(insert-file-contents prot-register-save-file)
|
||||
(read (buffer-string)))))
|
||||
|
||||
(defun prot-register-watcher (symbol newval operation where)
|
||||
(when (and (eq operation 'set) (null where) newval)
|
||||
(prot-register-store)))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-register-persist-mode
|
||||
"When enabled save `register-alist' to `prot-register-save-file' when a change occurs."
|
||||
:globat t
|
||||
(if prot-register-persist-mode
|
||||
(progn
|
||||
(add-variable-watcher 'register-alist #'prot-register-watcher)
|
||||
(add-hook 'kill-emacs-hook #'prot-register-store))
|
||||
(remove-variable-watcher 'register-alist #'prot-register-watcher)
|
||||
(remove-hook 'kill-emacs-hook #'prot-register-store)))
|
||||
|
||||
(provide 'prot-register)
|
||||
;;; prot-register.el ends here
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-scratch.el --- Scratch buffers for editable major mode of choice -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -27,11 +27,6 @@
|
|||
;; Set up a scratch buffer for an editable major mode of choice. The
|
||||
;; idea is based on the `scratch.el' package by Ian Eure:
|
||||
;; <https://github.com/ieure/scratch-el>.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-search.el --- Extensions to isearch, replace, grep for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2020-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2020-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my isearch.el, replace.el, and grep.el extensions, for
|
||||
;; use in my Emacs setup: <https://protesilaos.com/emacs/dotemacs>.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -94,51 +89,6 @@ search started."
|
|||
(isearch-pop-state)))
|
||||
(isearch-update))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-search-isearch-repeat-forward (&optional arg)
|
||||
"Move forward, keeping point at the beginning of the match.
|
||||
Optionally move to ARGth match in the given direction."
|
||||
(interactive "p")
|
||||
(when (and isearch-forward isearch-other-end)
|
||||
(goto-char isearch-other-end))
|
||||
(isearch-repeat-forward (or arg 1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-search-isearch-repeat-backward (&optional arg)
|
||||
"Move backward, keeping point at the beginning of the match.
|
||||
Optionally move to ARGth match in the given direction."
|
||||
(interactive "p")
|
||||
(when (and (not isearch-forward) isearch-other-end)
|
||||
(goto-char isearch-other-end))
|
||||
(isearch-repeat-backward (or arg 1)))
|
||||
|
||||
(defmacro prot-search-isearch-occurrence (name edge &optional doc)
|
||||
"Construct function for moving to `isearch' occurrence.
|
||||
NAME is the name of the function. EDGE is either the beginning
|
||||
or the end of the buffer. Optional DOC is the resulting
|
||||
function's docstring."
|
||||
`(defun ,name (&optional arg)
|
||||
,doc
|
||||
(interactive "p")
|
||||
(let ((x (or arg 1))
|
||||
(command (intern (format "isearch-%s-of-buffer" ,edge))))
|
||||
(isearch-forward-symbol-at-point)
|
||||
(funcall command x))))
|
||||
|
||||
(prot-search-isearch-occurrence
|
||||
prot-search-isearch-beginning-of-buffer
|
||||
"beginning"
|
||||
"Run `isearch-beginning-of-buffer' for the symbol at point.
|
||||
With numeric ARG, move to ARGth occurrence counting from the
|
||||
beginning of the buffer.")
|
||||
|
||||
(prot-search-isearch-occurrence
|
||||
prot-search-isearch-end-of-buffer
|
||||
"end"
|
||||
"Run `isearch-end-of-buffer' for the symbol at point.
|
||||
With numeric ARG, move to ARGth occurrence counting from the
|
||||
end of the buffer.")
|
||||
|
||||
;;;; Replace/Occur
|
||||
|
||||
(defvar prot-search-markup-replacements
|
||||
|
|
@ -195,6 +145,31 @@ boundaries, else start from point to the end of the buffer."
|
|||
;; (isearch-forward-symbol-at-point)
|
||||
;; (isearch-query-replace-regexp))
|
||||
|
||||
(defvar prot-search-collect-history nil
|
||||
"Minibuffer history for `prot-search-collect'.")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-search-collect (regexp)
|
||||
"Return matches for REGEXP as a substring in the current buffer.
|
||||
Put the results in a *prot-search-collect* buffer."
|
||||
(interactive
|
||||
(list
|
||||
(format "\\<\\w*%s\\w*\\>" (read-string "Collect REGEXP: " nil 'prot-search-collect-history))))
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(let ((matches nil))
|
||||
(while (re-search-forward regexp nil t)
|
||||
(push (match-string 0) matches))
|
||||
(when matches
|
||||
(setq matches (nreverse matches))
|
||||
(let ((buffer (get-buffer-create "*prot-search-collect*")))
|
||||
(with-current-buffer buffer
|
||||
(erase-buffer)
|
||||
(dolist (match matches)
|
||||
(insert match)
|
||||
(insert "\n")))
|
||||
(display-buffer buffer))))))
|
||||
|
||||
(autoload 'goto-address-mode "goto-addr")
|
||||
|
||||
;;;###autoload
|
||||
|
|
@ -333,9 +308,10 @@ Also see `prot-search-grep-todo-keywords'."
|
|||
(defun prot-search-outline ()
|
||||
"Go to the line of the given outline using completion."
|
||||
(interactive)
|
||||
(when-let ((selection (prot-search--outline-prompt))
|
||||
(line (string-to-number (car (split-string selection "\t")))))
|
||||
(goto-line line)
|
||||
(when-let* ((selection (prot-search--outline-prompt))
|
||||
(line (string-to-number (car (split-string selection "\t")))))
|
||||
(goto-char (point-min))
|
||||
(forward-line (- line 1))
|
||||
(run-hooks 'prot-search-outline-hook)))
|
||||
|
||||
;;;; Grep
|
||||
|
|
@ -528,18 +504,22 @@ and function as links to the context they reference."
|
|||
prot-search--find-file-names-args
|
||||
"dired")
|
||||
|
||||
;; (defun prot-search-find-grep-file (regexp)
|
||||
;; "Use find to produce list of files that include REGEXP."
|
||||
;; (interactive
|
||||
;; (list
|
||||
;; (read-regexp "Find and grep for REGEXP: " nil 'prot-search--find-grep-hist)))
|
||||
;; (let ((files (process-lines "find"
|
||||
;; "-type" "f"
|
||||
;; "-exec" "grep" "-nHE" "--color=auto" (format "'%s" regexp) " "
|
||||
;; "-ls" " "
|
||||
;; "{};")
|
||||
;; ))
|
||||
;; (find-file (completing-read "Find file: "files))))
|
||||
;; TODO 2026-08-15: I want to revise all of the above functions that
|
||||
;; involve find/grep. Is `process-lines' the better option?
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-search-grep-to-dired (regexp)
|
||||
"Produce a Dired buffer with files matching REGEXP.
|
||||
Recursively search for files from the current directory using the find
|
||||
program."
|
||||
(interactive
|
||||
(list
|
||||
(read-regexp "Find and grep for REGEXP: " nil 'prot-search--find-grep-hist)))
|
||||
(let ((files (process-lines "find"
|
||||
"-type" "f"
|
||||
"-exec" "grep" "-nHE" "--color=auto" regexp
|
||||
"-ls" "{}" ";")))
|
||||
(dired (cons default-directory files))))
|
||||
|
||||
(provide 'prot-search)
|
||||
;;; prot-search.el ends here
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-shell.el --- M-x shell extensions for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my shell.el extensions, for use in my Emacs setup:
|
||||
;; <https://protesilaos.com/emacs/dotemacs>.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -109,18 +104,24 @@ Only account for the history Emacs knows about, ignoring
|
|||
(defun prot-shell-track-cd (&rest _)
|
||||
"Track shell input of cd commands.
|
||||
Push `shell-last-dir' to `prot-shell-cd-directories'."
|
||||
(when-let ((input (prot-shell--last-input))
|
||||
((string-match-p "cd " input)))
|
||||
(when-let* ((input (prot-shell--last-input))
|
||||
((string-match-p "cd " input)))
|
||||
(push shell-last-dir prot-shell-cd-directories)))
|
||||
|
||||
(defun prot-shell-update-name-on-cd (&rest _)
|
||||
"Update the shell buffer name after a cd for use in `prot-shell'."
|
||||
(when-let* ((input (prot-shell--last-input))
|
||||
((string-match-p "cd " input)))
|
||||
(rename-buffer (format "*prot-shell in %s*" default-directory) :make-unique)))
|
||||
|
||||
(defvar prot-shell--cd-history nil
|
||||
"Minibuffer history for `prot-shell-cd'.")
|
||||
|
||||
(defun prot-shell--cd-prompt ()
|
||||
"Prompt for a directory among `prot-shell-cd-directories'."
|
||||
(if-let ((history prot-shell-cd-directories)
|
||||
(dirs (cons default-directory history))
|
||||
(def (if (listp dirs) (car dirs) shell-last-dir)))
|
||||
(if-let* ((history prot-shell-cd-directories)
|
||||
(dirs (cons default-directory history))
|
||||
(def (if (listp dirs) (car dirs) shell-last-dir)))
|
||||
(completing-read
|
||||
(format-prompt "Select directory" def)
|
||||
dirs nil :require-match nil 'prot-shell--cd-history def)
|
||||
|
|
@ -146,101 +147,104 @@ Push `shell-last-dir' to `prot-shell-cd-directories'."
|
|||
(defun prot-shell-cd-vc-root-dir ()
|
||||
"Change into the `vc-root-dir'."
|
||||
(interactive)
|
||||
(if-let ((root (prot-shell--get-vc-root-dir)))
|
||||
(if-let* ((root (prot-shell--get-vc-root-dir)))
|
||||
(prot-shell--insert-and-send "cd" root)
|
||||
(user-error "Cannot find the VC root of `%s'" default-directory)))
|
||||
|
||||
;;;; Bookmark support
|
||||
;; NOTE 2025-06-23: Emacs 31 supports shell bookmarks, so I no longer
|
||||
;; need this. I am keeping it here for reference.
|
||||
|
||||
;; NOTE 2023-08-18: I sent this to the Emacs maintainers as a patch
|
||||
;; (bug#65039). I received approval to proceed with the change, but I
|
||||
;; did not do it because a user reported an issue with SSH (TRAMP). I
|
||||
;; do not have access to SSH and am not familiar with such workflows.
|
||||
;; If/when that changes, I will try again. In the meantime, this is
|
||||
;; good code and it works for me.
|
||||
;; ;;;; Bookmark support
|
||||
;;
|
||||
;; ;; NOTE 2023-08-18: I sent this to the Emacs maintainers as a patch
|
||||
;; ;; (bug#65039). I received approval to proceed with the change, but I
|
||||
;; ;; did not do it because a user reported an issue with SSH (TRAMP). I
|
||||
;; ;; do not have access to SSH and am not familiar with such workflows.
|
||||
;; ;; If/when that changes, I will try again. In the meantime, this is
|
||||
;; ;; good code and it works for me.
|
||||
;;
|
||||
;; ;; Adapted from esh-mode.el
|
||||
;; (declare-function bookmark-prop-get "bookmark" (bookmark prop))
|
||||
;;
|
||||
;; (defun prot-shell-bookmark-name ()
|
||||
;; "Return name of bookmark based on currect directory."
|
||||
;; (format "prot-shell-%s"
|
||||
;; (file-name-nondirectory
|
||||
;; (directory-file-name
|
||||
;; (file-name-directory default-directory)))))
|
||||
;;
|
||||
;; (defvar sh-shell-file)
|
||||
;;
|
||||
;; (defun prot-shell-bookmark-make-record ()
|
||||
;; "Create a bookmark for the current Shell buffer."
|
||||
;; `(,(prot-shell-bookmark-name)
|
||||
;; (location . ,default-directory)
|
||||
;; (shell-file-name . ,sh-shell-file)
|
||||
;; (handler . prot-shell-bookmark-jump)))
|
||||
;;
|
||||
;; ;;;###autoload
|
||||
;; (defun prot-shell-bookmark-jump (bookmark)
|
||||
;; "Default BOOKMARK handler for Shell buffers."
|
||||
;; (let ((default-directory (bookmark-prop-get bookmark 'location))
|
||||
;; (explicit-shell-file-name (bookmark-prop-get bookmark 'shell-file-name)))
|
||||
;; (shell (get-buffer-create (car bookmark)))))
|
||||
;;
|
||||
;; (put 'prot-shell-bookmark-jump 'bookmark-handler-type "Shell")
|
||||
|
||||
;; Adapted from esh-mode.el
|
||||
(declare-function bookmark-prop-get "bookmark" (bookmark prop))
|
||||
|
||||
(defun prot-shell-bookmark-name ()
|
||||
"Return name of bookmark based on currect directory."
|
||||
(format "prot-shell-%s"
|
||||
(file-name-nondirectory
|
||||
(directory-file-name
|
||||
(file-name-directory default-directory)))))
|
||||
|
||||
(defvar sh-shell-file)
|
||||
|
||||
(defun prot-shell-bookmark-make-record ()
|
||||
"Create a bookmark for the current Shell buffer."
|
||||
`(,(prot-shell-bookmark-name)
|
||||
(location . ,default-directory)
|
||||
(shell-file-name . ,sh-shell-file)
|
||||
(handler . prot-shell-bookmark-jump)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-shell-bookmark-jump (bookmark)
|
||||
"Default BOOKMARK handler for Shell buffers."
|
||||
(let ((default-directory (bookmark-prop-get bookmark 'location))
|
||||
(explicit-shell-file-name (bookmark-prop-get bookmark 'shell-file-name)))
|
||||
(shell (get-buffer-create (car bookmark)))))
|
||||
|
||||
(put 'prot-shell-bookmark-jump 'bookmark-handler-type "Shell")
|
||||
|
||||
;;;; Convert YouTube links to Invidious
|
||||
|
||||
(defvar prot-shell-invidious-domains
|
||||
'("invidious.io.lol"
|
||||
"invidious.lunar.icu"
|
||||
"iv.nboeck.de"
|
||||
"vid.priv.au"
|
||||
"invidious.tiekoetter.com"
|
||||
"inv.in.projectsegfau.lt"
|
||||
"onion.tube"
|
||||
"yt.artemislena.eu"
|
||||
"invidious.no-logs.com"
|
||||
"yewtu.be"
|
||||
"invidious.projectsegfau.lt"
|
||||
"yt.oelrichsgarcia.de"
|
||||
"invidious.0011.lt"
|
||||
"inv.zzls.xyz"
|
||||
"inv.bp.projectsegfau.lt"
|
||||
"invidious.flokinet.to"
|
||||
"iv.ggtyler.dev"
|
||||
"invidious.slipfox.xyz"
|
||||
"vid.puffyan.us"
|
||||
"inv.pistasjis.net"
|
||||
"inv.citw.lgbt"
|
||||
"invidious.protokolla.fi"
|
||||
"inv.makerlab.tech"
|
||||
"inv.tux.pizza"
|
||||
"invidious.privacydev.net")
|
||||
"List of Invidious domains.")
|
||||
|
||||
(defvar prot-shell-youtube-domains
|
||||
'("www.youtube.com"
|
||||
"youtu.be")
|
||||
"List of YouTube domains.")
|
||||
|
||||
(defvar prot-shell-yt-invidious-domains
|
||||
(append prot-shell-youtube-domains
|
||||
prot-shell-invidious-domains)
|
||||
"List of YouTube and Invidious domains.")
|
||||
|
||||
(defun prot-shell--get-random-invidious-instance ()
|
||||
"Return `random' index from `prot-shell-invidious-domains'."
|
||||
(nth
|
||||
(random (length prot-shell-invidious-domains))
|
||||
prot-shell-invidious-domains))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-shell-invidious ()
|
||||
"Convert `prot-shell-yt-invidious-domains' into a random Invidious instance."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(goto-char (line-beginning-position))
|
||||
(while (re-search-forward (regexp-opt prot-shell-yt-invidious-domains) (line-end-position) :no-error)
|
||||
(replace-match (prot-shell--get-random-invidious-instance)))))
|
||||
;; ;;;; Convert YouTube links to Invidious
|
||||
;;
|
||||
;; (defvar prot-shell-invidious-domains
|
||||
;; '("invidious.io.lol"
|
||||
;; "invidious.lunar.icu"
|
||||
;; "iv.nboeck.de"
|
||||
;; "vid.priv.au"
|
||||
;; "invidious.tiekoetter.com"
|
||||
;; "inv.in.projectsegfau.lt"
|
||||
;; "onion.tube"
|
||||
;; "yt.artemislena.eu"
|
||||
;; "invidious.no-logs.com"
|
||||
;; "yewtu.be"
|
||||
;; "invidious.projectsegfau.lt"
|
||||
;; "yt.oelrichsgarcia.de"
|
||||
;; "invidious.0011.lt"
|
||||
;; "inv.zzls.xyz"
|
||||
;; "inv.bp.projectsegfau.lt"
|
||||
;; "invidious.flokinet.to"
|
||||
;; "iv.ggtyler.dev"
|
||||
;; "invidious.slipfox.xyz"
|
||||
;; "vid.puffyan.us"
|
||||
;; "inv.pistasjis.net"
|
||||
;; "inv.citw.lgbt"
|
||||
;; "invidious.protokolla.fi"
|
||||
;; "inv.makerlab.tech"
|
||||
;; "inv.tux.pizza"
|
||||
;; "invidious.privacydev.net")
|
||||
;; "List of Invidious domains.")
|
||||
;;
|
||||
;; (defvar prot-shell-youtube-domains
|
||||
;; '("www.youtube.com"
|
||||
;; "youtu.be")
|
||||
;; "List of YouTube domains.")
|
||||
;;
|
||||
;; (defvar prot-shell-yt-invidious-domains
|
||||
;; (append prot-shell-youtube-domains
|
||||
;; prot-shell-invidious-domains)
|
||||
;; "List of YouTube and Invidious domains.")
|
||||
;;
|
||||
;; (defun prot-shell--get-random-invidious-instance ()
|
||||
;; "Return `random' index from `prot-shell-invidious-domains'."
|
||||
;; (nth
|
||||
;; (random (length prot-shell-invidious-domains))
|
||||
;; prot-shell-invidious-domains))
|
||||
;;
|
||||
;; ;;;###autoload
|
||||
;; (defun prot-shell-invidious ()
|
||||
;; "Convert `prot-shell-yt-invidious-domains' into a random Invidious instance."
|
||||
;; (interactive)
|
||||
;; (save-excursion
|
||||
;; (goto-char (line-beginning-position))
|
||||
;; (while (re-search-forward (regexp-opt prot-shell-yt-invidious-domains) (line-end-position) :no-error)
|
||||
;; (replace-match (prot-shell--get-random-invidious-instance)))))
|
||||
|
||||
;;;; Built-in Emacs commands
|
||||
|
||||
|
|
@ -298,14 +302,23 @@ input, otherwise perform buffer motion."
|
|||
(interactive "^p")
|
||||
(prot-shell--history-or-motion 'comint-next-input 'next-line arg))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-shell ()
|
||||
"Like `shell' but always start a new shell.
|
||||
Name the shell buffer after the `default-directory'. If the name of
|
||||
that buffer already exists, then reuse it."
|
||||
(interactive)
|
||||
(with-current-buffer (shell (format "*prot-shell in %s*" default-directory))
|
||||
(add-hook 'comint-output-filter-functions #'prot-shell-update-name-on-cd nil :local)))
|
||||
|
||||
;;;; Minor mode setup
|
||||
|
||||
(defvar-keymap prot-shell-mode-map
|
||||
:doc "Key map for `prot-shell-mode'."
|
||||
:doc "keymap for `prot-shell-mode'."
|
||||
"<up>" #'prot-shell-up-dwim
|
||||
"<down>" #'prot-shell-down-dwim
|
||||
"C-c C-d" #'prot-shell-cd
|
||||
"C-c C-i" #'prot-shell-invidious
|
||||
;; "C-c C-i" #'prot-shell-invidious
|
||||
"C-c C-j" #'prot-shell-input-from-history
|
||||
"C-c C-." #'prot-shell-cd-vc-root-dir
|
||||
"C-c C-r" #'prot-shell-cd-vc-root-dir)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-simple.el --- Common commands for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2020-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2020-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -25,11 +25,6 @@
|
|||
;;; Commentary:
|
||||
;;
|
||||
;; Common commands for my Emacs: <https://protesilaos.com/emacs/dotemacs/>.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -57,6 +52,39 @@ Used by `prot-simple-inset-date'."
|
|||
|
||||
;;;; General commands
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-describe-at-point (symbol)
|
||||
"Describe the SYMBOL at point.
|
||||
If there is no symbol or the symbol at point does not satisfy `symbolp',
|
||||
prompt for one."
|
||||
(interactive (list (intern-soft (thing-at-point 'symbol))))
|
||||
(if symbol
|
||||
(describe-symbol symbol)
|
||||
(call-interactively 'describe-symbol)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-indent-dwim ()
|
||||
"Indent the current defun in `prog-mode' or paragraph in `text-mode'."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(cond
|
||||
((derived-mode-p 'prog-mode)
|
||||
(mark-defun))
|
||||
((derived-mode-p 'text-mode)
|
||||
(mark-paragraph)))
|
||||
(indent-for-tab-command)
|
||||
(deactivate-mark)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-sudo ()
|
||||
"Find the current file or directory using `sudo'."
|
||||
(interactive)
|
||||
(let ((destination (or buffer-file-name default-directory))
|
||||
(auto-save-default nil))
|
||||
(if (string= (file-remote-p destination 'method) "sudo")
|
||||
(user-error "Already using `sudo'")
|
||||
(find-file (format "/sudo::/%s" destination)))))
|
||||
|
||||
(defun prot-simple--mark (bounds)
|
||||
"Mark between BOUNDS as a cons cell of beginning and end positions."
|
||||
(push-mark (car bounds))
|
||||
|
|
@ -72,11 +100,11 @@ expression."
|
|||
(if (and (region-active-p)
|
||||
(eq last-command this-command))
|
||||
(ignore-errors (forward-sexp 1))
|
||||
(when-let ((thing (cond
|
||||
((thing-at-point 'url) 'url)
|
||||
((thing-at-point 'sexp) 'sexp)
|
||||
((thing-at-point 'string) 'string)
|
||||
((thing-at-point 'word) 'word))))
|
||||
(when-let* ((thing (cond
|
||||
((thing-at-point 'url) 'url)
|
||||
((thing-at-point 'sexp) 'sexp)
|
||||
((thing-at-point 'string) 'string)
|
||||
((thing-at-point 'word) 'word))))
|
||||
(prot-simple--mark (bounds-of-thing-at-point thing)))))
|
||||
|
||||
;;;###autoload
|
||||
|
|
@ -104,17 +132,24 @@ The DWIM behaviour of this command is as follows:
|
|||
(t
|
||||
(keyboard-quit))))
|
||||
|
||||
;; DEPRECATED 2023-12-26: I have not used `prot-simple-describe-symbol'
|
||||
;; since a very long time. The idea is fine, but having a key binding
|
||||
;; to provide a shortcut for C-h o RET is wasteful.
|
||||
|
||||
;; (autoload 'symbol-at-point "thingatpt")
|
||||
;;
|
||||
;; ;;;###autoload
|
||||
;; (defun prot-simple-describe-symbol ()
|
||||
;; "Run `describe-symbol' for the `symbol-at-point'."
|
||||
;; (interactive)
|
||||
;; (describe-symbol (symbol-at-point)))
|
||||
;;;###autoload
|
||||
(defun prot-simple-delete-window-dwim ()
|
||||
"Do What I Mean to delete the current THING.
|
||||
When there is more than one window, THING is a window.
|
||||
When there are more than one `tab-bar-mode' tabs, THING is a tab.
|
||||
Else THING is a frame if frames are more than one."
|
||||
(declare (interactive-only t))
|
||||
(interactive)
|
||||
(cond
|
||||
((length> (window-list) 1)
|
||||
(delete-window))
|
||||
((and (featurep 'tab-bar)
|
||||
(length> (tab-bar-tabs) 1))
|
||||
(tab-close))
|
||||
((length> (frame-list) 1)
|
||||
(delete-frame))
|
||||
(t
|
||||
(user-error "Nothing to delete"))))
|
||||
|
||||
;; DEPRECATED 2023-12-26: The `prot-simple-goto-definition' is a good
|
||||
;; idea but it needs more work. Ultimately though, it is easier to
|
||||
|
|
@ -154,8 +189,8 @@ The DWIM behaviour of this command is as follows:
|
|||
;;
|
||||
;; (defun prot-simple--number-replace (number amount operation)
|
||||
;; "Perform OPERATION on NUMBER at point by AMOUNT."
|
||||
;; (when-let ((bounds (bounds-of-thing-at-point 'number))
|
||||
;; (replacement (prot-simple--number-operate number amount operation)))
|
||||
;; (when-let* ((bounds (bounds-of-thing-at-point 'number))
|
||||
;; (replacement (prot-simple--number-operate number amount operation)))
|
||||
;; (delete-region (car bounds) (cdr bounds))
|
||||
;; (save-excursion
|
||||
;; (insert (number-to-string replacement)))))
|
||||
|
|
@ -218,6 +253,38 @@ When called interactively without a prefix numeric argument, N is
|
|||
|
||||
(make-obsolete 'prot-simple-copy-line-or-region 'prot-simple-copy-line "2023-09-26")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-kill-ring-save (&optional beg end)
|
||||
"Copy the current region or line.
|
||||
When the region is active, use `kill-ring-save' between the BEG and END
|
||||
positions. Otherwise, copy the current line."
|
||||
;; NOTE 2025-02-23: Using (interactive "r") returns an error before
|
||||
;; running the body of this function if there is no mark. This
|
||||
;; happens when visiting a file.
|
||||
(interactive
|
||||
(when (region-active-p)
|
||||
(list
|
||||
(region-beginning)
|
||||
(region-end))))
|
||||
(if (and beg end)
|
||||
(copy-region-as-kill beg end)
|
||||
(prot-simple-copy-line))
|
||||
(setq this-command 'kill-ring-save))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-kill-region (&optional beg end)
|
||||
"Do `kill-region' when the region is active, else `kill-ring-save' symbol at point."
|
||||
(interactive
|
||||
(when (region-active-p)
|
||||
(list
|
||||
(region-beginning)
|
||||
(region-end))))
|
||||
(if (and beg end)
|
||||
(kill-region beg end)
|
||||
(prot-simple-mark-sexp)
|
||||
(copy-region-as-kill (region-beginning) (region-end)))
|
||||
(setq this-command 'kill-ring-save))
|
||||
|
||||
(defun prot-simple--duplicate-buffer-substring (boundaries)
|
||||
"Duplicate buffer substring between BOUNDARIES.
|
||||
BOUNDARIES is a cons cell representing buffer positions."
|
||||
|
|
@ -238,7 +305,8 @@ BOUNDARIES is a cons cell representing buffer positions."
|
|||
(prot-simple--duplicate-buffer-substring
|
||||
(if (region-active-p)
|
||||
(cons (region-beginning) (region-end))
|
||||
(cons (line-beginning-position) (line-end-position)))))
|
||||
(cons (line-beginning-position) (line-end-position))))
|
||||
(setq this-command 'yank))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-yank-replace-line-or-region ()
|
||||
|
|
@ -249,7 +317,8 @@ This command can then be followed by the standard
|
|||
(if (use-region-p)
|
||||
(delete-region (region-beginning) (region-end))
|
||||
(delete-region (line-beginning-position) (line-end-position)))
|
||||
(yank))
|
||||
(yank)
|
||||
(setq this-command 'yank))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-multi-line-below ()
|
||||
|
|
@ -269,7 +338,75 @@ This command can then be followed by the standard
|
|||
(defun prot-simple-kill-line-backward ()
|
||||
"Kill from point to the beginning of the line."
|
||||
(interactive)
|
||||
(kill-line 0))
|
||||
(kill-line 0)
|
||||
(setq this-command 'kill-line))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-copy-line-forward (n)
|
||||
"Copy from point to the end of the Nth line.
|
||||
Without numeric prefix argument N, operate on the current line."
|
||||
(interactive "p")
|
||||
(let ((point (point))
|
||||
(end (line-end-position n))
|
||||
(max (point-max)))
|
||||
(copy-region-as-kill
|
||||
point
|
||||
(if (> end max)
|
||||
max
|
||||
end)))
|
||||
(setq this-command 'kill-ring-save))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-copy-line-backward (n)
|
||||
"Copy from point to the beginning of the Nth line.
|
||||
Without numeric prefix argument N, operate on the current line."
|
||||
(interactive "p")
|
||||
(let ((point (point))
|
||||
(beg (line-beginning-position n))
|
||||
(min (point-min)))
|
||||
(copy-region-as-kill
|
||||
point
|
||||
(if (< beg min)
|
||||
min
|
||||
beg)))
|
||||
(setq this-command 'kill-ring-save))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-delete-line ()
|
||||
"Delete (not kill) from point to the end of the line."
|
||||
(interactive)
|
||||
(let* ((point (point))
|
||||
(end (line-end-position))
|
||||
(end+ (+ end 1)))
|
||||
(cond
|
||||
((> end+ (point-max)) (delete-region point end))
|
||||
((= point end) (delete-region point end+))
|
||||
(t (delete-region point end))))
|
||||
(setq this-command 'delete-region))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-delete-line-backward ()
|
||||
"Delete (not kill) from point to the beginning of the line."
|
||||
(interactive)
|
||||
(let* ((point (point))
|
||||
(beg (line-beginning-position))
|
||||
(beg- (- beg 1)))
|
||||
(cond
|
||||
((< beg- (point-min)) (delete-region beg point))
|
||||
((= point beg) (delete-region beg- point))
|
||||
(t (delete-region beg point))))
|
||||
(setq this-command 'delete-region))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-simple-auto-fill-visual-line-mode
|
||||
"Enable `visual-line-mode' and disable `auto-fill-mode' in the current buffer."
|
||||
:global nil
|
||||
(if prot-simple-auto-fill-visual-line-mode
|
||||
(progn
|
||||
(auto-fill-mode -1)
|
||||
(visual-line-mode 1))
|
||||
(auto-fill-mode 1)
|
||||
(visual-line-mode -1)))
|
||||
|
||||
;;;; Commands for text insertion or manipulation
|
||||
|
||||
|
|
@ -311,7 +448,7 @@ to `line-beginning-position'."
|
|||
(line-end-position) :no-error
|
||||
(prefix-numeric-value current-prefix-arg))
|
||||
(line-beginning-position))))
|
||||
(when-let ((regexp-end (prot-simple--pos-url-on-line char)))
|
||||
(when-let* ((regexp-end (prot-simple--pos-url-on-line char)))
|
||||
(goto-char regexp-end)
|
||||
(unless (looking-at ">")
|
||||
(insert ">")
|
||||
|
|
@ -457,9 +594,9 @@ demarcated by BEG and END."
|
|||
;; When the region is active, toggle the presence of STRING for each
|
||||
;; line in the region."
|
||||
;; (interactive (list (prot-simple-line-prefix-infer-or-prompt)))
|
||||
;; (if-let ((region-p (region-active-p))
|
||||
;; (beg (region-beginning))
|
||||
;; (end (line-number-at-pos (region-end))))
|
||||
;; (if-let* ((region-p (region-active-p))
|
||||
;; (beg (region-beginning))
|
||||
;; (end (line-number-at-pos (region-end))))
|
||||
;; (progn
|
||||
;; (goto-char beg)
|
||||
;; (push-mark (point))
|
||||
|
|
@ -479,11 +616,11 @@ demarcated by BEG and END."
|
|||
(end (pos-eol))
|
||||
diff-eol-point
|
||||
diff-eol-mark)
|
||||
(when-let (((use-region-p))
|
||||
(pos (point))
|
||||
(mrk (mark))
|
||||
(line-diff-mark-point (1+ (- (line-number-at-pos mrk)
|
||||
(line-number-at-pos pos)))))
|
||||
(when-let* (((use-region-p))
|
||||
(pos (point))
|
||||
(mrk (mark))
|
||||
(line-diff-mark-point (1+ (- (line-number-at-pos mrk)
|
||||
(line-number-at-pos pos)))))
|
||||
(if (> pos mrk)
|
||||
(setq start (pos-bol line-diff-mark-point)) ; pos-bol of where the mark is
|
||||
(setq end (pos-eol line-diff-mark-point))) ; pos-eol of the line where the mark is
|
||||
|
|
@ -510,15 +647,15 @@ demarcated by BEG and END."
|
|||
(defun prot-simple--move-line-user-error (boundary)
|
||||
"Return `user-error' with message accounting for BOUNDARY.
|
||||
BOUNDARY is a buffer position, expected to be `point-min' or `point-max'."
|
||||
(when-let ((bound (line-number-at-pos boundary))
|
||||
(scope (cond
|
||||
((and (use-region-p)
|
||||
(or (= (line-number-at-pos (point)) bound)
|
||||
(= (line-number-at-pos (mark)) bound)))
|
||||
"region is ")
|
||||
((= (line-number-at-pos (point)) bound)
|
||||
"")
|
||||
(t nil))))
|
||||
(when-let* ((bound (line-number-at-pos boundary))
|
||||
(scope (cond
|
||||
((and (use-region-p)
|
||||
(or (= (line-number-at-pos (point)) bound)
|
||||
(= (line-number-at-pos (mark)) bound)))
|
||||
"region is ")
|
||||
((= (line-number-at-pos (point)) bound)
|
||||
"")
|
||||
(t nil))))
|
||||
(user-error (format "Warning: %salready in the last line!" scope))))
|
||||
|
||||
(defun prot-simple-move-above-dwim (arg)
|
||||
|
|
@ -567,23 +704,22 @@ There is no dragging the character forward. This is the
|
|||
behaviour of `transpose-chars' when point is at the end of the
|
||||
line."
|
||||
(interactive)
|
||||
(transpose-chars -1)
|
||||
(forward-char))
|
||||
(if (eq (point) (line-end-position))
|
||||
(transpose-chars 1)
|
||||
(transpose-chars -1)
|
||||
(forward-char)))
|
||||
|
||||
;;;; Commands for paragraphs
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-unfill-region-or-paragraph (&optional beg end)
|
||||
"Unfill paragraph or, when active, the region.
|
||||
Join all lines in region delimited by BEG and END, if active,
|
||||
while respecting any empty lines (so multiple paragraphs are not
|
||||
joined, just unfilled). If no region is active, operate on the
|
||||
paragraph. The idea is to produce the opposite effect of both
|
||||
`fill-paragraph' and `fill-region'."
|
||||
(interactive "r")
|
||||
(defun prot-simple-unfill-region-or-paragraph ()
|
||||
"Unfill current paragraph or the active region."
|
||||
(interactive)
|
||||
(unless mark-ring ; needed when entering a new buffer
|
||||
(push-mark (point) t nil))
|
||||
(let ((fill-column most-positive-fixnum))
|
||||
(if (use-region-p)
|
||||
(fill-region beg end)
|
||||
(if (region-active-p)
|
||||
(fill-region (region-beginning) (region-end))
|
||||
(fill-paragraph))))
|
||||
|
||||
;;;; Commands for windows and pages
|
||||
|
|
@ -735,14 +871,14 @@ Name the buffer after the defun's symbol."
|
|||
(defun prot-simple-display-unsaved-buffers ()
|
||||
"Produce buffer menu listing unsaved file-visiting buffers."
|
||||
(interactive)
|
||||
(if-let ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
|
||||
(if-let* ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
|
||||
(prot-simple--display-unsaved-buffers unsaved-buffers "*Unsaved buffers*")
|
||||
(message "No unsaved buffers")))
|
||||
|
||||
(defun prot-simple-display-unsaved-buffers-on-exit (&rest _)
|
||||
"Produce buffer menu listing unsaved file-visiting buffers.
|
||||
Add this as :before advice to `save-buffers-kill-emacs'."
|
||||
(when-let ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
|
||||
(when-let* ((unsaved-buffers (prot-simple--get-unsaved-buffers)))
|
||||
(prot-simple--display-unsaved-buffers unsaved-buffers "*Unsaved buffers*")))
|
||||
|
||||
;;;###autoload
|
||||
|
|
@ -769,8 +905,13 @@ When called interactively, prompt for BUFFER."
|
|||
(let ((kill-buffer-query-functions nil))
|
||||
(kill-buffer (or buffer (current-buffer)))))
|
||||
|
||||
(make-obsolete
|
||||
'prot-simple-kill-buffer-current
|
||||
'prot-simple-kill-buffer-dwim
|
||||
"2025-11-04")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-kill-buffer-current (&optional arg)
|
||||
(defun prot-simple-kill-buffer-dwim (&optional arg)
|
||||
"Kill current buffer.
|
||||
With optional prefix ARG (\\[universal-argument]) delete the
|
||||
buffer's window as well. Kill the window regardless of ARG if it
|
||||
|
|
@ -778,11 +919,31 @@ satisfies `prot-common-window-small-p' and it has no previous
|
|||
buffers in its history."
|
||||
(interactive "P")
|
||||
(let ((kill-buffer-query-functions nil))
|
||||
(if (or (and (prot-common-window-small-p)
|
||||
(null (window-prev-buffers)))
|
||||
(and arg (not (one-window-p))))
|
||||
(kill-buffer-and-window)
|
||||
(kill-buffer))))
|
||||
(cond
|
||||
;; Is a tab whose last window's buffer is to be deleted.
|
||||
((and (one-window-p)
|
||||
(length> (tab-bar-tabs) 1))
|
||||
(let ((buffer (current-buffer)))
|
||||
(tab-close)
|
||||
(kill-buffer buffer)))
|
||||
;; Is an ancillary window that appeared for this buffer but is
|
||||
;; otherwise not supposed to be there.
|
||||
((and (prot-common-window-small-p)
|
||||
(null (window-prev-buffers)))
|
||||
(kill-buffer-and-window))
|
||||
(t
|
||||
(kill-buffer)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-copy-current-path ()
|
||||
"Copy the current absolute path.
|
||||
If in a file-visiting buffer, copy the variable `buffer-file-name'.
|
||||
Else copy the `default-directory'."
|
||||
(declare (interactive-only t))
|
||||
(interactive)
|
||||
(let ((path (or buffer-file-name default-directory)))
|
||||
(kill-new path)
|
||||
(message "Copied `%s'" (propertize path 'face 'success))))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-rename-file-and-buffer (name)
|
||||
|
|
@ -849,38 +1010,6 @@ counter-clockwise."
|
|||
(dotimes (i (- wincount 1))
|
||||
(window-swap-states (elt winlist i) (elt winlist (+ i 1))))))
|
||||
|
||||
;;;; Commands for files
|
||||
|
||||
(cl-defmethod register--type ((_regval vector)) 'vector)
|
||||
|
||||
(cl-defmethod register-val-describe ((val vector) _verbose)
|
||||
(if-let ((pos (aref val 2))
|
||||
(file (aref val 1)))
|
||||
(princ (format "%s at position %s" file pos))
|
||||
(princ "Garbage data")))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-file-to-register (register)
|
||||
"Store current location of file's point in REGISTER."
|
||||
(interactive (list (register-read-with-preview "File with point to register: ")))
|
||||
(set-register register (vector 'file-with-point (buffer-file-name) (point))))
|
||||
|
||||
(defvar prot-simple-file-to-register-jump-hook nil
|
||||
"Normal hook called after jumping to a file register.
|
||||
See `prot-simple-file-to-register'.")
|
||||
|
||||
;;;###autoload
|
||||
(cl-defmethod register-val-jump-to ((val vector) delete)
|
||||
"Handle how to jump to a location register.
|
||||
This is like the default, but does not ask to visit a file: it does it
|
||||
outright."
|
||||
(cond
|
||||
((eq (aref val 0) 'file-with-point)
|
||||
(find-file (aref val 1))
|
||||
(goto-char (aref val 2))
|
||||
(run-hooks 'prot-simple-file-to-register-jump-hook))
|
||||
(t (cl-call-next-method val delete))))
|
||||
|
||||
;;;; Commands of a general nature
|
||||
|
||||
(autoload 'color-rgb-to-hex "color")
|
||||
|
|
@ -918,5 +1047,135 @@ VARIANT is either `dark' or `light'."
|
|||
(interactive (list (prot-simple--list-accessible-colors-prompt)))
|
||||
(list-colors-display (prot-simple-accessible-colors variant)))
|
||||
|
||||
(defun prot-simple-update-package-repositories-pull (package package-directory buffer)
|
||||
"Pull PACKAGE which extends PACKAGE-DIRECTORY.
|
||||
Use BUFFER for standard output and return the exit code."
|
||||
(let ((default-directory package-directory))
|
||||
(message "Pulling %s from directory %s" package default-directory)
|
||||
(call-process "git" nil (list buffer t) nil "pull")))
|
||||
|
||||
(defun prot-simple-update-package-repositories-clone (package base-directory buffer)
|
||||
"Clone PACKAGE to an extension of BASE-DIRECTORY.
|
||||
Use BUFFER for standard output and return the exit code."
|
||||
(message "Cloning %s to directory %s" package base-directory)
|
||||
(call-process "git" nil (list buffer t) nil "clone" (format "git@github.com:protesilaos/%s %s" package base-directory)))
|
||||
|
||||
(define-error 'prot-package-no-update "Package could not be updated" 'error)
|
||||
|
||||
(defun prot-simple-update-package-repositories-subr (packages)
|
||||
"Pull or clone all repositories of my PACKAGES."
|
||||
(unless (executable-find "git")
|
||||
(user-error "Cannot find git program; install it first or add it to the $PATH; aborting"))
|
||||
(unless (getenv "SSH_AUTH_SOCK")
|
||||
(user-error "Cannot find $SSH_AUTH_SOCK; check your SSH connection; aborting"))
|
||||
(let ((stdout (get-buffer-create " *prot-simple-git-package-stdout*")))
|
||||
(dolist (package packages)
|
||||
(let* ((name (cond
|
||||
((symbolp package) (symbol-name package))
|
||||
((stringp package) package)
|
||||
(t (error "The `%s' is neither a symbol nor a string" package))))
|
||||
(common-directory (expand-file-name "~/Git/Projects/"))
|
||||
(package-directory (expand-file-name name common-directory))
|
||||
(exit-code (if (file-directory-p package-directory)
|
||||
(prot-simple-update-package-repositories-pull package package-directory stdout)
|
||||
(prot-simple-update-package-repositories-clone package common-directory stdout))))
|
||||
(condition-case error-data
|
||||
(when (> exit-code 0)
|
||||
(signal 'prot-package-no-update (list (format "Package `%s' got exit code `%s'" package exit-code))))
|
||||
(:success
|
||||
(message "Updated `%s' repository" package))
|
||||
(prot-package-no-update
|
||||
;; TODO 2025-11-06: Is it safe to stash changes outright?
|
||||
;; I think it is fine, but maybe there is a case where this
|
||||
;; can lead to data loss?
|
||||
(when (file-directory-p package-directory)
|
||||
(let ((default-directory package-directory))
|
||||
(unless (fboundp 'vc-git-stash)
|
||||
(require 'vc-git))
|
||||
(vc-git-stash (format "prot-package-update %s: %s" (format-time-string "%FT%T") package))))
|
||||
(message "Tried to stash changes in package `%s' because: %s" package (cdr error-data)))
|
||||
((error user-error)
|
||||
(message "The package `%s' returned error data: %s" package error-data))
|
||||
(quit
|
||||
(message "Aborted by the user")))))))
|
||||
|
||||
(defvar prot-simple-update-package-repositories-prompt-history nil
|
||||
"Minibuffer history of `prot-simple-update-package-repositories-prompt'.")
|
||||
|
||||
(defun prot-simple-update-package-repositories-prompt ()
|
||||
"Prompt for packages among `prot-emacs-my-packages'."
|
||||
(let ((default (car prot-simple-update-package-repositories-prompt-history)))
|
||||
(completing-read-multiple
|
||||
(format-prompt "Select packages" default)
|
||||
prot-emacs-my-packages
|
||||
nil t nil
|
||||
'prot-simple-update-package-repositories-prompt-history
|
||||
default)))
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-simple-update-some-or-all-of-my-package-repositories (packages &optional all-packages)
|
||||
"Prompt for PACKAGES among `prot-emacs-my-packages' to pull or clone.
|
||||
With a universal prefix argument for ALL-PACKAGES, do not prompt for packages and
|
||||
update them all instead."
|
||||
(interactive
|
||||
(list
|
||||
(if current-prefix-arg
|
||||
prot-emacs-my-packages
|
||||
(prot-simple-update-package-repositories-prompt))))
|
||||
(prot-simple-update-package-repositories-subr packages))
|
||||
|
||||
;;;; Global minor mode to override keymaps
|
||||
|
||||
(defvar prot-simple-override-mode-map (make-sparse-keymap)
|
||||
"keymap for `prot-simple-override-mode'.
|
||||
Enable that mode to have its key bindings take effect over those of the
|
||||
major mode.")
|
||||
|
||||
(define-minor-mode prot-simple-override-mode
|
||||
"Enable the `prot-simple-override-mode-map'."
|
||||
:init-value nil
|
||||
:global t
|
||||
:keymap prot-simple-override-mode-map)
|
||||
|
||||
;;;; Preview hexadecimal RGB colours
|
||||
|
||||
;; FIXME 2026-01-10: Why is this not working in Org tables? The
|
||||
;; properties are applied but the face is not displayed.
|
||||
(defun prot-simple-hex-fontify (limit)
|
||||
"Fontify the # of a hexadecimal RGB colour up to LIMIT."
|
||||
(when-let* ((found (re-search-forward "\\(#\\)\\([a-fA-F0-9]\\{6\\}\\)" limit t))
|
||||
(color (match-string-no-properties 0))
|
||||
(beg (match-beginning 1))
|
||||
(end (match-end 1))
|
||||
(face (list :background color :foreground color :inverse-video t)))
|
||||
(add-text-properties beg end (list 'face face 'font-lock-face face 'display "######"))
|
||||
found))
|
||||
|
||||
(defun prot-simple-hex-unfontify ()
|
||||
"Fontify the # of a hexadecimal RGB colour up to LIMIT."
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while-let ((found (re-search-forward "\\(#\\)\\([a-fA-F0-9]\\{6\\}\\)" nil t))
|
||||
(color (match-string-no-properties 0))
|
||||
(beg (match-beginning 1))
|
||||
(end (match-end 1))
|
||||
(face (list :background color :foreground color :inverse-video t)))
|
||||
(put-text-property beg end 'display "#")
|
||||
found)))
|
||||
|
||||
(defvar prot-simple-hex-color-keywords
|
||||
'((prot-simple-hex-fontify))
|
||||
"Font lock keywords for `prot-simple-hex-color-mode'.")
|
||||
|
||||
(define-minor-mode prot-simple-hex-color-mode
|
||||
"Preview hexadecimal colour values."
|
||||
:global nil
|
||||
:init-value nil
|
||||
(if prot-simple-hex-color-mode
|
||||
(font-lock-add-keywords nil prot-simple-hex-color-keywords)
|
||||
(prot-simple-hex-unfontify)
|
||||
(font-lock-remove-keywords nil prot-simple-hex-color-keywords))
|
||||
(font-lock-flush (point-min) (point-max)))
|
||||
|
||||
(provide 'prot-simple)
|
||||
;;; prot-simple.el ends here
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-spell.el --- Spelling-related extensions for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2021-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my spelling-related extensions, for use in my Emacs
|
||||
;; setup: https://protesilaos.com/emacs/dotemacs.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -101,5 +96,42 @@ function. Then you can control the buffer's specifics via
|
|||
|
||||
(advice-add #'ispell-display-buffer :override #'prot-spell-ispell-display-buffer)
|
||||
|
||||
;;;; French punctuation
|
||||
|
||||
;; TODO 2026-07-01: Maybe extend the command to check for all sorts of
|
||||
;; rules, in accordance with the Charte orthotypographique du Journal
|
||||
;; officiel:
|
||||
;; <https://www.legifrance.gouv.fr/contenu/Media/Files/autour-de-la-loi/legislatif-et-reglementaire/charte_typographique_jo_janvier_2021.pdf?__cf_chl_f_tk=YcOBi.sMMWmrN5FxCQMM8YDz.OyTC62mXKuZnjZ2rvs-1782901834-1.0.1.1-IzZmLEEkwksUf1sSm5dljuFqSZjDPjXfLMYkIdbOa.Y>.
|
||||
;;
|
||||
;; Though that is not a simple search+replace operation...
|
||||
;;
|
||||
;; For the following:
|
||||
;;
|
||||
;; «L’espace insécable permet d’éviter la séparation entre deux
|
||||
;; éléments. Elle s’utilisera, entre autres, à l’intérieur des
|
||||
;; guillemets français, au sein des nombres, après les dito, avec les
|
||||
;; tirets d’incise.»
|
||||
(defvar prot-spell-french-grammar-replacements
|
||||
'(("\\([^[:punct:][:space:]]\\)\\?" . "\\1 ?")
|
||||
("\\([^[:punct:][:space:]]\\):" . "\\1 :")
|
||||
("\\([^[:punct:][:space:]]\\);" . "\\1 ;")
|
||||
("\\([^[:punct:][:space:]]\\)!" . "\\1 !")
|
||||
("«\\([^[:punct:][:space:]]\\)" . "« \\1")
|
||||
("\\([^[:punct:][:space:]]\\)»" . "\\1 »")
|
||||
("\\([^[:punct:][:space:]]\\)%" . "\\1 %"))
|
||||
"Add no-break space to relevant punctuation.")
|
||||
|
||||
;;;###autoload
|
||||
(defun prot-spell-french-punctuation ()
|
||||
"Apply the `prot-spell-french-grammar-replacements' across the buffer."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(pcase-dolist (`(,target . ,replacement) prot-spell-french-grammar-replacements)
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward target nil t)
|
||||
(replace-match replacement))))))
|
||||
|
||||
(provide 'prot-spell)
|
||||
;;; prot-spell.el ends here
|
||||
|
|
|
|||
80
emacs/.emacs.d/prot-lisp/prot-streaming.el
Normal file
80
emacs/.emacs.d/prot-lisp/prot-streaming.el
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
;;; prot-streaming.el --- Tweaks for when I do videos or share my screen -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
||||
;; This file is NOT part of GNU Emacs.
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; This covers my streaming-related extensions, for use in my Emacs
|
||||
;; setup: https://protesilaos.com/emacs/dotemacs.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'display-line-numbers)
|
||||
(require 'hl-line)
|
||||
|
||||
(defcustom prot-streaming-mode-modes
|
||||
'(text-mode prog-mode dired-mode comint-mode)
|
||||
"Major modes where `prot-streaming-mode' takes effect.")
|
||||
|
||||
(defun prot-streaming-mode--display-line-numbers (arg)
|
||||
"Call `display-line-numbers-mode' with ARG.
|
||||
Do it for all buffers whose major mode is one among `prot-streaming-mode-modes'."
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
(when (derived-mode-p prot-streaming-mode-modes)
|
||||
(display-line-numbers-mode arg)))))
|
||||
|
||||
(defun prot-streaming--get-mode-hooks ()
|
||||
"Return hooks belonging to `prot-streaming-mode-modes'."
|
||||
(mapcar
|
||||
(lambda (symbol)
|
||||
(intern (format "%s-hook" symbol)))
|
||||
prot-streaming-mode-modes))
|
||||
|
||||
(defun prot-streaming-mode--affect-hooks (add-or-remove)
|
||||
"Apply ADD-OR-REMOVE to `prot-streaming-mode-hooks'.
|
||||
ADD-OR-REMOVE is the keyword `:add' or `:remove', which correspond to
|
||||
`add-hook' and `remove-hook'."
|
||||
(let ((fn (pcase add-or-remove
|
||||
(:add #'add-hook)
|
||||
(:remove #'remove-hook)
|
||||
(_ (error "ADD-OR-REMOVE must be either `:add' or `:remove'")))))
|
||||
(dolist (hook (prot-streaming--get-mode-hooks))
|
||||
(funcall fn hook #'display-line-numbers-mode))))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode prot-streaming-mode
|
||||
"Enable extras for streaming."
|
||||
:global t
|
||||
:init-value nil
|
||||
(if prot-streaming-mode
|
||||
(progn
|
||||
(prot-streaming-mode--display-line-numbers 1)
|
||||
(prot-streaming-mode--affect-hooks :add)
|
||||
(global-hl-line-mode 1))
|
||||
(prot-streaming-mode--display-line-numbers -1)
|
||||
(prot-streaming-mode--affect-hooks :remove)
|
||||
(global-hl-line-mode -1)))
|
||||
|
||||
(provide 'prot-streaming)
|
||||
;;; prot-streaming.el ends here
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-vertico.el --- Custom Vertico extras -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -23,11 +23,6 @@
|
|||
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -40,7 +35,8 @@
|
|||
:prompt ""
|
||||
:separator ""
|
||||
:ellipsis ""
|
||||
:no-match "")))
|
||||
:no-match ""))
|
||||
(vertico-preselect . prompt))
|
||||
"List of configurations for minimal Vertico multiform.
|
||||
The minimal view is intended to be more private or less
|
||||
revealing. This is important when, for example, a prompt shows
|
||||
|
|
@ -53,38 +49,29 @@ command or use the commands `prot-vertico-private-next' and
|
|||
automatically.")
|
||||
|
||||
(defvar prot-vertico-multiform-maximal
|
||||
'((vertico-count . 10)
|
||||
'(reverse
|
||||
(vertico-count . 10)
|
||||
(vertico-preselect . directory)
|
||||
(vertico-resize . t))
|
||||
"List of configurations for maximal Vertico multiform.")
|
||||
|
||||
(defun prot-vertico--match-directory (str)
|
||||
"Match directory delimiter in STR."
|
||||
(string-suffix-p "/" str))
|
||||
|
||||
;; From the Vertico documentation.
|
||||
(defun prot-vertico-sort-directories-first (files)
|
||||
"Sort directories before FILES."
|
||||
(setq files (vertico-sort-alpha files))
|
||||
(nconc (seq-filter #'prot-vertico--match-directory files)
|
||||
(seq-remove #'prot-vertico--match-directory files)))
|
||||
|
||||
(defun prot-vertico-private-next ()
|
||||
"Like `vertico-next' but toggle vertical view if needed.
|
||||
This is done to accommodate `prot-vertico-multiform-minimal'."
|
||||
(interactive)
|
||||
(if vertico-unobtrusive-mode
|
||||
(let ((vertico--index 0))
|
||||
(vertico-multiform-vertical)
|
||||
(vertico-next 1))
|
||||
(vertico-next 1)))
|
||||
|
||||
(defun prot-vertico-private-previous ()
|
||||
"Like `vertico-previous' but toggle vertical view if needed.
|
||||
"Like `vertico-next' but toggle reverse vertical view if needed.
|
||||
This is done to accommodate `prot-vertico-multiform-minimal'."
|
||||
(interactive)
|
||||
(if vertico-unobtrusive-mode
|
||||
(progn
|
||||
(vertico-multiform-vertical)
|
||||
(vertico-multiform-reverse)
|
||||
(vertico-next 1))
|
||||
(vertico-next 1)))
|
||||
|
||||
(defun prot-vertico-private-previous ()
|
||||
"Like `vertico-previous' but toggle reverse vertical view if needed.
|
||||
This is done to accommodate `prot-vertico-multiform-minimal'."
|
||||
(interactive)
|
||||
(if vertico-unobtrusive-mode
|
||||
(progn
|
||||
(vertico-multiform-reverse)
|
||||
(vertico-previous 1))
|
||||
(vertico-previous 1)))
|
||||
|
||||
|
|
@ -95,8 +82,43 @@ This is done to accommodate `prot-vertico-multiform-minimal'."
|
|||
(if (and vertico-unobtrusive-mode (> vertico--total 1))
|
||||
(progn
|
||||
(minibuffer-complete)
|
||||
(vertico-multiform-vertical))
|
||||
(prot-vertico-private-next))
|
||||
(vertico-insert)))
|
||||
|
||||
(defun prot-vertico-private-exit ()
|
||||
"Exit with the candidate if `prot-vertico-multiform-minimal'.
|
||||
If there are more candidates that match the given input, expand the
|
||||
minibuffer to show the remaining candidates and select the first one.
|
||||
Else do `vertico-exit'."
|
||||
(interactive)
|
||||
(cond
|
||||
((and (= vertico--total 1)
|
||||
(not (eq 'file (vertico--metadata-get 'category))))
|
||||
(minibuffer-complete)
|
||||
(vertico-exit))
|
||||
((and vertico-unobtrusive-mode
|
||||
(not minibuffer--require-match)
|
||||
(or (string-empty-p (minibuffer-contents))
|
||||
minibuffer-default
|
||||
(eq vertico-preselect 'directory)
|
||||
(eq vertico-preselect 'prompt)))
|
||||
(vertico-exit-input))
|
||||
((and vertico-unobtrusive-mode (> vertico--total 1))
|
||||
(minibuffer-complete-and-exit)
|
||||
(prot-vertico-private-next))
|
||||
(t
|
||||
(vertico-exit))))
|
||||
|
||||
;; (cl-defgeneric vertico--display-candidates (lines)
|
||||
;; "Reverse the default vertico view of LINES."
|
||||
;; (move-overlay vertico--candidates-ov (point-min) (point-min))
|
||||
;; ;; (setq lines (nreverse lines))
|
||||
;; (unless (eq vertico-resize t)
|
||||
;; (setq lines (nconc (make-list (max 0 (- vertico-count (length lines))) "\n") lines)))
|
||||
;; (let ((string (apply #'concat lines)))
|
||||
;; (add-face-text-property 0 (length string) 'default 'append string)
|
||||
;; (overlay-put vertico--candidates-ov 'before-string string)
|
||||
;; (overlay-put vertico--candidates-ov 'after-string nil)))
|
||||
|
||||
(provide 'prot-vertico)
|
||||
;;; prot-vertico.el ends here
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
;;; prot-window.el --- Display-buffer and window-related extensions for my dotemacs -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2023 Protesilaos Stavrou
|
||||
;; Copyright (C) 2023-2026 Protesilaos
|
||||
|
||||
;; Author: Protesilaos Stavrou <info@protesilaos.com>
|
||||
;; Author: Protesilaos <info@protesilaos.com>
|
||||
;; URL: https://protesilaos.com/emacs/dotemacs
|
||||
;; Version: 0.1.0
|
||||
;; Package-Requires: ((emacs "30.1"))
|
||||
|
|
@ -26,11 +26,6 @@
|
|||
;;
|
||||
;; This covers my window and display-buffer extensions, for use in my
|
||||
;; Emacs setup: https://protesilaos.com/emacs/dotemacs.
|
||||
;;
|
||||
;; Remember that every piece of Elisp that I write is for my own
|
||||
;; educational and recreational purposes. I am not a programmer and I
|
||||
;; do not recommend that you copy any of this if you are not certain of
|
||||
;; what it does.
|
||||
|
||||
;;; Code:
|
||||
|
||||
|
|
@ -48,7 +43,7 @@ number (integer or floating point) or a function.")
|
|||
|
||||
(defun prot-window--get-window-size (key)
|
||||
"Extract the value of KEY from `prot-window-window-sizes'."
|
||||
(when-let ((value (plist-get prot-window-window-sizes key)))
|
||||
(when-let* ((value (plist-get prot-window-window-sizes key)))
|
||||
(cond
|
||||
((functionp value)
|
||||
(funcall value))
|
||||
|
|
@ -101,6 +96,21 @@ This as the action function in a `display-buffer-alist' entry."
|
|||
(when (apply fn args)
|
||||
(throw 'success fn))))))
|
||||
|
||||
(defun prot-window-display-new (buffer alist)
|
||||
"Display buffer in a new window if possible."
|
||||
(when-let* ((root (frame-root-window))
|
||||
(window (ignore-errors
|
||||
(or (split-window-horizontally nil root)
|
||||
(split-window-below nil root)))))
|
||||
(window--display-buffer buffer window 'window alist)))
|
||||
|
||||
(defun prot-window-display-other-or-current (&rest args)
|
||||
"Display buffer in the current or other window.
|
||||
Use the other window if only one window is present."
|
||||
(if (length> (window-list) 1)
|
||||
(apply #'display-buffer-same-window args)
|
||||
(apply #'display-buffer-pop-up-window args)))
|
||||
|
||||
(defun prot-window-shell-or-term-p (buffer &rest _)
|
||||
"Check if BUFFER is a shell or terminal.
|
||||
This is a predicate function for `buffer-match-p', intended for
|
||||
|
|
@ -148,9 +158,9 @@ call NAME as a function."
|
|||
|
||||
(defun prot-window--get-new-shell-buffer ()
|
||||
"Return buffer name for `shell' buffers."
|
||||
(if-let ((buffers (prot-window--get-shell-buffers))
|
||||
(buffers-length (length buffers))
|
||||
((>= buffers-length 1)))
|
||||
(if-let* ((buffers (prot-window--get-shell-buffers))
|
||||
(buffers-length (length buffers))
|
||||
((>= buffers-length 1)))
|
||||
(format "*shell*<%s>" (1+ buffers-length))
|
||||
"*shell*"))
|
||||
|
||||
|
|
@ -159,7 +169,7 @@ call NAME as a function."
|
|||
(let ((name (prot-window--get-new-shell-buffer)))
|
||||
(shell name)
|
||||
(set-frame-name name)
|
||||
(when-let ((buffer (get-buffer name)))
|
||||
(when-let* ((buffer (get-buffer name)))
|
||||
(with-current-buffer buffer
|
||||
(add-hook
|
||||
'delete-frame-functions
|
||||
|
|
@ -173,13 +183,16 @@ call NAME as a function."
|
|||
nil
|
||||
:local)))))
|
||||
|
||||
;;;###autoload (autoload 'prot-window-coach "prot-window")
|
||||
(prot-window-define-full-frame coach
|
||||
(let ((buffer (get-buffer-create "*scratch for coach*")))
|
||||
;;;###autoload (autoload 'prot-window-meeting "prot-window")
|
||||
(prot-window-define-full-frame meeting
|
||||
(let ((buffer (get-buffer-create "*scratch for meeting*")))
|
||||
(with-current-buffer buffer
|
||||
(funcall initial-major-mode))
|
||||
(funcall initial-major-mode)
|
||||
(when (and (zerop (buffer-size))
|
||||
(stringp initial-scratch-message))
|
||||
(insert initial-scratch-message)))
|
||||
(display-buffer buffer)
|
||||
(set-frame-name "Coach")))
|
||||
(set-frame-name "Meeting")))
|
||||
|
||||
;; REVIEW 2023-06-25: Does this merit a user option? I don't think I
|
||||
;; will ever set it to the left. It feels awkward there.
|
||||
|
|
@ -197,5 +210,59 @@ call NAME as a function."
|
|||
|
||||
(add-hook 'after-make-frame-functions 'prot-window-no-minibuffer-scroll-bar)
|
||||
|
||||
;;;; Run commands in a popup frame (via emacsclient)
|
||||
|
||||
(defun prot-window-delete-popup-frame (&rest _)
|
||||
"Kill selected selected frame if it has parameter `prot-window-popup-frame'.
|
||||
Use this function via a hook."
|
||||
(dolist (frame (frame-list))
|
||||
(when (frame-parameter frame 'prot-window-popup-frame)
|
||||
(delete-frame frame))))
|
||||
|
||||
;; NOTE 2025-02-11: Also see `prot-vertico-with-buffer-mode', which
|
||||
;; extends this to use a full-frame buffer for Vertico.
|
||||
(defmacro prot-window-define-with-popup-frame (command)
|
||||
"Define function which calls COMMAND in a new frame.
|
||||
Make the new frame have the `prot-window-popup-frame' parameter."
|
||||
`(defun ,(intern (format "prot-window-popup-%s" command)) ()
|
||||
,(format "Run `%s' in a popup frame with `prot-window-popup-frame' parameter.
|
||||
Also see `prot-window-delete-popup-frame'." command)
|
||||
(interactive)
|
||||
(let ((frame (make-frame '((prot-window-popup-frame . t)
|
||||
(explicit-name . t)
|
||||
(name . "prot-window-popup")))))
|
||||
(select-frame frame)
|
||||
(switch-to-buffer " prot-window-hidden-buffer-for-popup-frame")
|
||||
(condition-case nil
|
||||
(call-interactively ',command)
|
||||
((quit error user-error)
|
||||
(delete-frame frame))))))
|
||||
|
||||
(declare-function org-capture "org-capture" (&optional goto keys))
|
||||
(declare-function tmr "tmr" (time &optional description acknowledgep))
|
||||
(declare-function prot-project-switch "prot-project" (directory))
|
||||
|
||||
;;;###autoload (autoload 'prot-window-popup-org-capture "prot-window")
|
||||
(prot-window-define-with-popup-frame org-capture) ; defines command `prot-window-popup-org-capture'
|
||||
|
||||
;;;###autoload (autoload 'prot-window-popup-tmr "prot-window")
|
||||
(prot-window-define-with-popup-frame tmr) ; defines command `prot-window-popup-tmr'
|
||||
|
||||
;;;###autoload (autoload 'prot-window-popup-prot-project-switch "prot-window")
|
||||
(prot-window-define-with-popup-frame prot-project-switch) ; defines command `prot-window-popup-prot-project-switch'
|
||||
|
||||
(defun prot-window-set-delete-popup-hook (feature hook)
|
||||
"Set up `prot-window-delete-popup-frame' for FEATURE with HOOK."
|
||||
(with-eval-after-load feature
|
||||
(add-hook hook #'prot-window-delete-popup-frame)))
|
||||
|
||||
(defvar org-capture-after-finalize-hook)
|
||||
(defvar tmr-timer-created-functions)
|
||||
(defvar prot-project-switch-hook)
|
||||
|
||||
(prot-window-set-delete-popup-hook 'org-capture 'org-capture-after-finalize-hook)
|
||||
(prot-window-set-delete-popup-hook 'tmr 'tmr-timer-created-functions)
|
||||
(prot-window-set-delete-popup-hook 'prot-project 'prot-project-switch-hook)
|
||||
|
||||
(provide 'prot-window)
|
||||
;;; prot-window.el ends here
|
||||
|
|
|
|||
21
emacs/.local/share/applications/emacsclient.desktop
Normal file
21
emacs/.local/share/applications/emacsclient.desktop
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[Desktop Entry]
|
||||
Name=Prot Emacsclient
|
||||
GenericName=Text Editor
|
||||
Comment=Edit text
|
||||
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;x-scheme-handler/org-protocol;
|
||||
Exec=sh -c "if [ -n \\"\\$*\\" ]; then exec /usr/local/bin/emacsclient --alternate-editor="emacs" --display=\\"\\$DISPLAY\\" \\"\\$@\\"; else exec /usr/local/bin/emacsclient --alternate-editor="emacs" --create-frame; fi" sh %F
|
||||
Icon=emacs
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Categories=Development;TextEditor;
|
||||
StartupWMClass=Emacs
|
||||
Keywords=emacsclient;
|
||||
Actions=new-window;new-instance;
|
||||
|
||||
[Desktop Action new-window]
|
||||
Name=New Window
|
||||
Exec=/usr/local/bin/emacsclient --alternate-editor="emacs" --create-frame %F
|
||||
|
||||
[Desktop Action new-instance]
|
||||
Name=New Instance
|
||||
Exec=emacs %F
|
||||
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
<description>Set substitute fonts for Latin</description>
|
||||
|
||||
<!-- Iosevka Comfy is my custom build of Iosevka: <https://git.sr.ht/~protesilaos/iosevka-comfy> -->
|
||||
<!-- Aporetic is my custom build of Iosevka: <https://github.com/protesilaos/aporetic> -->
|
||||
|
||||
<!-- Serif -->
|
||||
<alias>
|
||||
<family>Iosevka Comfy Motion Duo</family>
|
||||
<family>Aporetic Serif</family>
|
||||
<default><family>serif</family></default>
|
||||
</alias>
|
||||
<alias>
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<!-- Sans Serif -->
|
||||
<alias>
|
||||
<family>Iosevka Comfy Duo</family>
|
||||
<family>Aporetic Sans</family>
|
||||
<default><family>sans-serif</family></default>
|
||||
</alias>
|
||||
<alias>
|
||||
|
|
@ -47,8 +47,7 @@
|
|||
|
||||
<!-- Monospaced -->
|
||||
<alias>
|
||||
<!-- We need the "fixed" variant for programs such as XTerm -->
|
||||
<family>Iosevka Comfy Motion Fixed</family>
|
||||
<family>Aporetic Serif Mono</family>
|
||||
<default><family>monospace</family></default>
|
||||
</alias>
|
||||
<alias>
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
<description>Set preferable fonts for Latin</description>
|
||||
|
||||
<!-- Iosevka Comfy is my custom build of Iosevka: <https://git.sr.ht/~protesilaos/iosevka-comfy> -->
|
||||
<!-- Aporetic is my custom build of Iosevka: <https://github.com/protesilaos/aporetic> -->
|
||||
|
||||
<alias binding="strong">
|
||||
<family>serif</family>
|
||||
<prefer>
|
||||
<family>Iosevka Comfy Motion Duo</family>
|
||||
<family>Aporetic Serif</family>
|
||||
<family>Noto Serif</family>
|
||||
<family>Liberation Serif</family>
|
||||
</prefer>
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
<alias binding="strong">
|
||||
<family>sans-serif</family>
|
||||
<prefer>
|
||||
<family>Iosevka Comfy Duo</family>
|
||||
<family>Aporetic Sans</family>
|
||||
<family>FiraGO</family>
|
||||
<family>Roboto</family>
|
||||
<family>Noto Sans</family>
|
||||
|
|
@ -36,8 +36,7 @@
|
|||
<alias binding="strong">
|
||||
<family>monospace</family>
|
||||
<prefer>
|
||||
<family>Iosevka Comfy Motion Fixed</family>
|
||||
<!-- My custom build of Hack: <https://git.sr.ht/~protesilaos/hack-font-mod> -->
|
||||
<family>Aporetic Serif Mono</family>
|
||||
<family>Hack</family>
|
||||
<family>Source Code Pro</family>
|
||||
</prefer>
|
||||
|
|
|
|||
159
gnome/.local/bin/gnome_custom_keys.sh
Executable file
159
gnome/.local/bin/gnome_custom_keys.sh
Executable file
|
|
@ -0,0 +1,159 @@
|
|||
#!/bin/bash
|
||||
|
||||
# gnome_custom_keys.sh --- My custom key bindings for GNOME
|
||||
#
|
||||
# Copyright (C) 2025-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
### Commentary:
|
||||
#
|
||||
# My custom key bindings for GNOME. This may not be complete but it
|
||||
# fixes all the known keybinding-related conflicts I encounter
|
||||
# whenever I want to use Emacs inside of GNOME.
|
||||
#
|
||||
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
|
||||
|
||||
### Code:
|
||||
|
||||
gsettings set org.gnome.TextEditor keybindings 'default'
|
||||
gsettings set org.gnome.desktop.wm.keybindings activate-window-menu "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings always-on-top "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings begin-move "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings begin-resize "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings close "['<Super>q']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings cycle-group "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings cycle-group-backward "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings cycle-panels "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings cycle-panels-backward "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings cycle-windows "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings cycle-windows-backward "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings lower "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings maximize "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings maximize-horizontally "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings maximize-vertically "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings minimize "['<Super>h']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-center "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-ne "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-nw "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-se "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-corner-sw "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-down "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-left "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-right "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-monitor-up "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-side-e "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-side-n "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-side-s "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-side-w "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-1 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-10 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-11 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-12 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-2 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-3 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-4 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-5 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-6 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-7 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-8 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-9 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-down "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-last "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-left "['<Shift><Super>Left']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-right "['<Shift><Super>Right']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings move-to-workspace-up "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings panel-main-menu "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings panel-run-dialog "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings raise "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings raise-or-lower "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings set-spew-mark "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings show-desktop "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-applications "['<Super>Tab']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-applications-backward "['<Shift><Super>Tab']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-group "['<Super>grave']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-group-backward "['<Shift><Super>grave']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-input-source "['<Super>space', 'XF86Keyboard']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-input-source-backward "['<Shift><Super>space', '<Shift>XF86Keyboard']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-panels "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-panels-backward "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-1 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-10 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-11 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-12 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-2 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-3 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-4 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-5 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-6 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-7 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-8 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-9 "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-down "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-last "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-left "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-right "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-to-workspace-up "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-windows "['<Alt>Tab']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings switch-windows-backward "['<Shift><Alt>Tab']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings toggle-above "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings toggle-fullscreen "['<Super>f']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings toggle-maximized "['<Super>m']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings toggle-on-all-workspaces "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings toggle-shaded "['']"
|
||||
gsettings set org.gnome.desktop.wm.keybindings unmaximize "['']"
|
||||
gsettings set org.gnome.metacity.keybindings toggle-tiled-left "['<Super>Left']"
|
||||
gsettings set org.gnome.metacity.keybindings toggle-tiled-right "['<Super>Right']"
|
||||
gsettings set org.gnome.mutter.keybindings rotate-monitor "['']"
|
||||
gsettings set org.gnome.mutter.keybindings switch-monitor "['']"
|
||||
gsettings set org.gnome.mutter.keybindings tab-popup-cancel "['']"
|
||||
gsettings set org.gnome.mutter.keybindings tab-popup-select "['']"
|
||||
gsettings set org.gnome.mutter.keybindings toggle-tiled-left "['<Super>Left']"
|
||||
gsettings set org.gnome.mutter.keybindings toggle-tiled-right "['<Super>Right']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings restore-shortcuts "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-1 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-10 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-11 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-12 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-2 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-3 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-4 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-5 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-6 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-7 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-8 "['']"
|
||||
gsettings set org.gnome.mutter.wayland.keybindings switch-to-session-9 "['']"
|
||||
gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/']"
|
||||
gsettings set org.gnome.shell.keybindings focus-active-notification "['']"
|
||||
gsettings set org.gnome.shell.keybindings open-application-menu "['']"
|
||||
gsettings set org.gnome.shell.keybindings screenshot "['<Shift>Print']"
|
||||
gsettings set org.gnome.shell.keybindings screenshot-window "['<Alt>Print']"
|
||||
gsettings set org.gnome.shell.keybindings shift-overview-down "['']"
|
||||
gsettings set org.gnome.shell.keybindings shift-overview-up "['']"
|
||||
gsettings set org.gnome.shell.keybindings show-screen-recording-ui "['']"
|
||||
gsettings set org.gnome.shell.keybindings show-screenshot-ui "['Print']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-1 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-2 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-3 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-4 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-5 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-6 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-7 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-8 "['']"
|
||||
gsettings set org.gnome.shell.keybindings switch-to-application-9 "['']"
|
||||
gsettings set org.gnome.shell.keybindings toggle-application-view "['<Super>a']"
|
||||
gsettings set org.gnome.shell.keybindings toggle-message-tray "['']"
|
||||
gsettings set org.gnome.shell.keybindings toggle-overview "['<Super>d']"
|
||||
gsettings set org.gtk.Settings.Debug enable-inspector-keybinding false
|
||||
gsettings set org.gtk.gtk4.Settings.Debug enable-inspector-keybinding true
|
||||
3
gnupg/.config/systemd/user/README-gpg
Normal file
3
gnupg/.config/systemd/user/README-gpg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Use this:
|
||||
|
||||
systemctl --user enable dirmngr.socket gpg-agent.socket gpg-agent-ssh.socket
|
||||
3
gtk/.config/gtk-3.0/gtk.css
Normal file
3
gtk/.config/gtk-3.0/gtk.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
VteTerminal, vte-terminal {
|
||||
padding: 15px 30px;
|
||||
}
|
||||
3
gtk/.config/gtk-4.0/gtk.css
Normal file
3
gtk/.config/gtk-4.0/gtk.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
VteTerminal, vte-terminal {
|
||||
padding: 15px 30px;
|
||||
}
|
||||
3
mbsync/.config/systemd/user/README-mbsync
Normal file
3
mbsync/.config/systemd/user/README-mbsync
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
To enable mbsync with notmuch integration, do the following:
|
||||
|
||||
systemctl --user enable mbsync.timer
|
||||
|
|
@ -3,7 +3,7 @@ Description=Mailbox synchronization timer
|
|||
|
||||
[Timer]
|
||||
OnBootSec=2m
|
||||
OnUnitActiveSec=5m
|
||||
OnUnitActiveSec=2m
|
||||
Unit=mbsync.service
|
||||
|
||||
[Install]
|
||||
37
mbsync/.local/bin/mbsync_remove_uid.sh
Executable file
37
mbsync/.local/bin/mbsync_remove_uid.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
# mbsync_remove_uid.sh --- Remove the UID from email files.
|
||||
#
|
||||
# Copyright (C) 2025-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
### Commentary:
|
||||
#
|
||||
# Remove the mbsync UID from email files. THIS IS RISKY SO PLEASE MAKE
|
||||
# SURE YOU UNDERSTAND WHAT YOU ARE DOING.
|
||||
#
|
||||
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
|
||||
|
||||
### Code:
|
||||
|
||||
my_mail_directory="$HOME"/.mail
|
||||
|
||||
while IFS= read -r -d $'\0' file
|
||||
do
|
||||
if [ -f "$file" ]
|
||||
then
|
||||
mv "$file" "${file//,U=[0-9]*:/:}"
|
||||
fi
|
||||
done < <(find "$my_mail_directory" -type f -iname "*,U=[0-9]*" -print0)
|
||||
143
mbsync/.mbsyncrc
143
mbsync/.mbsyncrc
|
|
@ -1,82 +1,89 @@
|
|||
IMAPAccount pub
|
||||
# Gandi
|
||||
####################################################################################################
|
||||
IMAPAccount gandi
|
||||
Host mail.gandi.net
|
||||
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $(NF-2); exit; }'"
|
||||
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $NF; exit; }'"
|
||||
SSLType IMAPS
|
||||
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $(NF-2); exit; }'"
|
||||
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $NF; exit; }'"
|
||||
TLSType IMAPS
|
||||
|
||||
IMAPStore pub-remote
|
||||
Account pub
|
||||
IMAPStore gandi-remote
|
||||
Account gandi
|
||||
|
||||
MaildirStore pub-local
|
||||
MaildirStore gandi-local
|
||||
Subfolders Verbatim
|
||||
# The trailing "/" is important
|
||||
Path ~/.mail/pub/
|
||||
Inbox ~/.mail/pub/Inbox
|
||||
# The trailing "/" in 'Path' is important. The path given to 'Path'
|
||||
# must exist, but the path given to 'Inbox' is created automatically
|
||||
# by 'mbsync'.
|
||||
Path ~/.mail/gandi/
|
||||
Inbox ~/.mail/gandi/Inbox
|
||||
|
||||
Channel pub
|
||||
Far :pub-remote:
|
||||
Near :pub-local:
|
||||
Channel gandi
|
||||
Far :gandi-remote:
|
||||
Near :gandi-local:
|
||||
# Include everything
|
||||
Patterns *
|
||||
Sync All
|
||||
Create Both
|
||||
Create Near
|
||||
Remove Both
|
||||
Expunge Both
|
||||
SyncState *
|
||||
|
||||
##########
|
||||
# NOTE 2024-12-01: I do not use the others, but have tested them in
|
||||
# the past. I keep the settings here in case I decide to test the
|
||||
# functionality again.
|
||||
|
||||
IMAPAccount inf
|
||||
Host mail.gandi.net
|
||||
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $(NF-2); exit; }'"
|
||||
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $NF; exit; }'"
|
||||
SSLType IMAPS
|
||||
|
||||
IMAPStore inf-remote
|
||||
Account inf
|
||||
|
||||
MaildirStore inf-local
|
||||
Subfolders Verbatim
|
||||
# The trailing "/" is important
|
||||
Path ~/.mail/inf/
|
||||
Inbox ~/.mail/inf/Inbox
|
||||
|
||||
Channel inf
|
||||
Far :inf-remote:
|
||||
Near :inf-local:
|
||||
# Include everything
|
||||
Pattern *
|
||||
Sync All
|
||||
Create Both
|
||||
Remove Both
|
||||
Expunge Both
|
||||
SyncState *
|
||||
|
||||
##########
|
||||
|
||||
IMAPAccount prv
|
||||
Host mail.gandi.net
|
||||
UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $(NF-2); exit; }'"
|
||||
PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $NF; exit; }'"
|
||||
SSLType IMAPS
|
||||
|
||||
IMAPStore prv-remote
|
||||
Account prv
|
||||
|
||||
MaildirStore prv-local
|
||||
Subfolders Verbatim
|
||||
# The trailing "/" is important
|
||||
Path ~/.mail/prv/
|
||||
Inbox ~/.mail/prv/Inbox
|
||||
|
||||
Channel prv
|
||||
Far :prv-remote:
|
||||
Near :prv-local:
|
||||
# Include everything
|
||||
Patterns *
|
||||
Sync All
|
||||
Create Both
|
||||
Remove Both
|
||||
Expunge Both
|
||||
SyncState *
|
||||
# # Gmail
|
||||
# ####################################################################################################
|
||||
# IMAPAccount gmail
|
||||
# Host imap.gmail.com
|
||||
# UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $(NF-2); exit; }'"
|
||||
# PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $NF; exit; }'"
|
||||
# SSLType IMAPS
|
||||
#
|
||||
# IMAPStore gmail-remote
|
||||
# Account gmail
|
||||
#
|
||||
# MaildirStore gmail-local
|
||||
# Subfolders Verbatim
|
||||
# # The trailing "/" is important
|
||||
# Path ~/.mail/gmail/
|
||||
# Inbox ~/.mail/gmail/Inbox
|
||||
#
|
||||
# Channel gmail
|
||||
# Far :gmail-remote:
|
||||
# Near :gmail-local:
|
||||
# # Include everything
|
||||
# Patterns *
|
||||
# Sync All
|
||||
# Create Near
|
||||
# Remove Near
|
||||
# Expunge Near
|
||||
# SyncState *
|
||||
|
||||
# # Mailbox
|
||||
# ####################################################################################################
|
||||
# IMAPAccount mailbox
|
||||
# Host imap.mailbox.org
|
||||
# UserCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $(NF-2); exit; }'"
|
||||
# PassCmd "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $NF; exit; }'"
|
||||
# SSLType IMAPS
|
||||
#
|
||||
# IMAPStore mailbox-remote
|
||||
# Account mailbox
|
||||
#
|
||||
# MaildirStore mailbox-local
|
||||
# Subfolders Verbatim
|
||||
# # The trailing "/" is important
|
||||
# Path ~/.mail/mailbox/
|
||||
# Inbox ~/.mail/mailbox/Inbox
|
||||
#
|
||||
# Channel mailbox
|
||||
# Far :mailbox-remote:
|
||||
# Near :mailbox-local:
|
||||
# # Include everything
|
||||
# Patterns *
|
||||
# Sync All
|
||||
# Create Near
|
||||
# Remove Near
|
||||
# Expunge Near
|
||||
# SyncState *
|
||||
|
|
|
|||
133
mpv/.config/mpv/input.conf
Normal file
133
mpv/.config/mpv/input.conf
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
MBTN_LEFT ignore
|
||||
MBTN_LEFT_DBL ignore
|
||||
MBTN_RIGHT ignore
|
||||
MBTN_BACK ignore
|
||||
MBTN_FORWARD ignore
|
||||
|
||||
WHEEL_UP ignore
|
||||
WHEEL_DOWN ignore
|
||||
WHEEL_LEFT ignore
|
||||
WHEEL_RIGHT ignore
|
||||
|
||||
RIGHT seek 5 # seek 5 seconds forward
|
||||
LEFT seek -5 # seek 5 seconds backward
|
||||
UP seek 60 # seek 1 minute forward
|
||||
DOWN seek -60 # seek 1 minute backward
|
||||
|
||||
Shift+RIGHT ignore
|
||||
Shift+LEFT ignore
|
||||
Shift+UP ignore
|
||||
Shift+DOWN ignore
|
||||
Ctrl+LEFT ignore
|
||||
Ctrl+RIGHT ignore
|
||||
Ctrl+Shift+LEFT ignore
|
||||
Ctrl+Shift+RIGHT ignore
|
||||
Alt+left ignore
|
||||
Alt+right ignore
|
||||
Alt+up ignore
|
||||
Alt+down ignore
|
||||
Alt++ ignore
|
||||
Alt+- ignore
|
||||
Alt+BS ignore
|
||||
PGUP ignore
|
||||
PGDWN ignore
|
||||
Shift+PGUP ignore
|
||||
Shift+PGDWN ignore
|
||||
[ ignore
|
||||
] ignore
|
||||
{ ignore
|
||||
} ignore
|
||||
BS ignore
|
||||
Shift+BS ignore
|
||||
Shift+Ctrl+BS ignore
|
||||
q ignore
|
||||
Q ignore
|
||||
q ignore
|
||||
ESC ignore
|
||||
ESC ignore
|
||||
p ignore
|
||||
. ignore
|
||||
, ignore
|
||||
SPACE cycle pause # toggle pause/playback mode
|
||||
> ignore
|
||||
ENTER ignore
|
||||
< ignore
|
||||
O ignore
|
||||
o ignore
|
||||
P ignore
|
||||
i ignore
|
||||
I ignore
|
||||
` ignore
|
||||
z ignore
|
||||
Z ignore
|
||||
x ignore
|
||||
ctrl++ ignore
|
||||
ctrl+- ignore
|
||||
Shift+g ignore
|
||||
Shift+f ignore
|
||||
9 ignore
|
||||
/ ignore
|
||||
0 ignore
|
||||
* ignore
|
||||
m ignore
|
||||
1 ignore
|
||||
2 ignore
|
||||
3 ignore
|
||||
4 ignore
|
||||
5 ignore
|
||||
6 ignore
|
||||
7 ignore
|
||||
8 ignore
|
||||
Alt+0 ignore
|
||||
Alt+1 ignore
|
||||
Alt+2 ignore
|
||||
d ignore
|
||||
r ignore
|
||||
R ignore
|
||||
t ignore
|
||||
v ignore
|
||||
Alt+v ignore
|
||||
V ignore
|
||||
u ignore
|
||||
j ignore
|
||||
J ignore
|
||||
SHARP ignore
|
||||
_ ignore
|
||||
T ignore
|
||||
f ignore
|
||||
s ignore
|
||||
S ignore
|
||||
Ctrl+s ignore
|
||||
Alt+s ignore
|
||||
w ignore
|
||||
W ignore
|
||||
e ignore
|
||||
A ignore
|
||||
POWER ignore
|
||||
PLAY ignore
|
||||
PAUSE ignore
|
||||
PLAYPAUSE ignore
|
||||
PLAYONLY ignore
|
||||
PAUSEONLY ignore
|
||||
STOP ignore
|
||||
FORWARD ignore
|
||||
REWIND ignore
|
||||
NEXT ignore
|
||||
PREV ignore
|
||||
VOLUME_UP ignore
|
||||
VOLUME_DOWN ignore
|
||||
MUTE ignore
|
||||
CLOSE_WIN quit
|
||||
ctrl+w ignore
|
||||
E ignore
|
||||
l ignore
|
||||
L ignore
|
||||
ctrl+c ignore
|
||||
DEL ignore
|
||||
ctrl+h ignore
|
||||
F8 ignore
|
||||
F9 ignore
|
||||
! ignore
|
||||
@ ignore
|
||||
? ignore
|
||||
? ignore
|
||||
|
|
@ -1,36 +1,43 @@
|
|||
# See my mbsync config, which is reflectd here.
|
||||
# See my mbsync config, which is reflected here.
|
||||
# https://github.com/protesilaos/dotfiles
|
||||
|
||||
defaults
|
||||
auth on
|
||||
protocol smtp
|
||||
tls on
|
||||
# FIXME 2023-08-08: I cannot send the message if `tls_starttls' is on.
|
||||
# Why?
|
||||
tls_starttls off
|
||||
tls_starttls on
|
||||
|
||||
# Gandi
|
||||
####################################################################################################
|
||||
account gandi
|
||||
host mail.gandi.net
|
||||
port 465
|
||||
port 587
|
||||
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $(NF-2); exit; }')
|
||||
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $(NF-2); exit; }')
|
||||
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prot-gandi/ { print $NF; exit; }'"
|
||||
|
||||
# pub
|
||||
account pub
|
||||
# FIXME 2023-08-08: Optimise this?
|
||||
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $(NF-2); exit; }')
|
||||
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $(NF-2); exit; }')
|
||||
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/pub/ { print $NF; exit; }'"
|
||||
# NOTE 2024-12-01: I do not use the others, but have tested them in
|
||||
# the past. I keep the settings here in case I decide to test the
|
||||
# functionality again.
|
||||
|
||||
# inf
|
||||
account inf
|
||||
# FIXME 2023-08-08: Optimise this?
|
||||
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $(NF-2); exit; }')
|
||||
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $(NF-2); exit; }')
|
||||
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/inf/ { print $NF; exit; }'"
|
||||
# # Gmail
|
||||
# ####################################################################################################
|
||||
# account gmail
|
||||
# host smtp.gmail.com
|
||||
# port 587
|
||||
# eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $(NF-2); exit; }')
|
||||
# eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $(NF-2); exit; }')
|
||||
# passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/gmail/ { print $NF; exit; }'"
|
||||
|
||||
# # Mailbox
|
||||
# ####################################################################################################
|
||||
# account mailbox
|
||||
# host smtp.mailbox.org
|
||||
# port 465
|
||||
# eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $(NF-2); exit; }')
|
||||
# eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $(NF-2); exit; }')
|
||||
# passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/mailbox/ { print $NF; exit; }'"
|
||||
|
||||
# prv
|
||||
account prv
|
||||
# FIXME 2023-08-08: Optimise this?
|
||||
eval echo from $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $(NF-2); exit; }')
|
||||
eval echo user $(gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $(NF-2); exit; }')
|
||||
passwordeval "gpg -q --for-your-eyes-only -d ~/.authinfo.gpg | awk -F ' ' '/prv/ { print $NF; exit; }'"
|
||||
|
||||
# Set a default account (I copied from man msmtp)
|
||||
account default : pub
|
||||
account default : gandi
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
# post-new --- Notmuch rules that run after notmuch new
|
||||
# post-new --- Notmuch rules that run after notmuch-new(1)
|
||||
#
|
||||
# Copyright (c) 2021-2023 Protesilaos Stavrou <info@protesilaos.com>
|
||||
# Copyright (C) 2021-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -17,19 +17,26 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
## Commentary:
|
||||
### Commentary:
|
||||
#
|
||||
# The order of those commands matters. Maybe I will write something
|
||||
# more sophisticated in the future.
|
||||
# Note that the order of those commands matters.
|
||||
#
|
||||
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
|
||||
|
||||
### Code:
|
||||
|
||||
# remove "unread" from "replied"
|
||||
notmuch tag -unread -new -- tag:replied
|
||||
|
||||
# tag all "new" messages "inbox" and "unread"
|
||||
notmuch tag +inbox +unread -new -- tag:new
|
||||
while IFS= read -r -d $'\0' file
|
||||
do
|
||||
if [[ "$file" != *R* ]]; then
|
||||
notmuch tag +unread -- "path:${file}"
|
||||
fi
|
||||
done < <(notmuch search --output=files --format=text0 tag:new)
|
||||
|
||||
notmuch tag +inbox -new -- tag:new
|
||||
|
||||
# tag my replies as "sent"
|
||||
# notmuch tag -new -unread +inbox +sent -- '(from:"*@protesilaos.com*" not to:"*@protesilaos.com*" not tag:list)'
|
||||
|
|
@ -44,19 +51,4 @@ notmuch tag +coach -- date:2023-01-20..now subject:/[Tt]utor\|[Cc]oach\|[Ll]esso
|
|||
|
||||
# Another "mostly okay" but not 100% correct way of identifying my
|
||||
# package-related messages.
|
||||
notmuch tag +package -- "(to:~protesilaos/*@lists.sr.ht or to:protesilaos/*noreply.github.com or to:*incoming.gitlab.com) not tag:archived not tag:list"
|
||||
|
||||
|
||||
# mailing lists
|
||||
## emacs
|
||||
notmuch tag -inbox +list +emacs -- from:emacs-devel@gnu.org or to:emacs-devel@gnu.org
|
||||
notmuch tag -inbox +list +emacs -- from:emacs-orgmode@gnu.org or to:emacs-orgmode@gnu.org
|
||||
notmuch tag -inbox +list +emacs -- 'to:"/*@debbugs.gnu.org*/"'
|
||||
notmuch tag -inbox +list +emacs -- from:emacs-humanities@gnu.org or to:emacs-humanities@gnu.org not to:emacs-humanities-owner@gnu.org
|
||||
|
||||
## Others
|
||||
notmuch tag -inbox +list +notmuch -- from:notmuch@notmuchmail.org or to:notmuch@notmuchmail.org
|
||||
notmuch tag -inbox +list +sourcehut -- 'to:"/*~sircmpwn/sr.ht-discuss@lists.sr.ht*/"'
|
||||
|
||||
# include mailing lists sent to me into the "inbox"
|
||||
notmuch tag +inbox -- '(to:"*@protesilaos.com*" tag:list)'
|
||||
notmuch tag +package -- "(to:protesilaos/*noreply.github.com or to:*incoming.gitlab.com) not tag:archived not tag:list"
|
||||
|
|
|
|||
90
notmuch/.mail/.notmuch/hooks/pre-new
Executable file
90
notmuch/.mail/.notmuch/hooks/pre-new
Executable file
|
|
@ -0,0 +1,90 @@
|
|||
#!/bin/bash
|
||||
|
||||
# pre-new --- Notmuch rules that run before notmuch-new(1)
|
||||
#
|
||||
# Copyright (C) 2021-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
### Commentary:
|
||||
#
|
||||
# Move unwanted email to the appropriate local directory and tag it
|
||||
# accordingly. The order of those commands matters.
|
||||
#
|
||||
# Part of my dotfiles: <https://github.com/protesilaos/dotfiles>.
|
||||
|
||||
### Code:
|
||||
|
||||
if [ "$HOSTNAME" = "kronos" ]
|
||||
then
|
||||
mail_root_path=$(notmuch config get database.path)
|
||||
for tag in del spam
|
||||
do
|
||||
target_directory="${mail_root_path}/${tag}local/"
|
||||
|
||||
if [ ! -d "$target_directory" ]
|
||||
then
|
||||
mkdir -p "$target_directory"
|
||||
fi
|
||||
|
||||
while IFS= read -r -d $'\0' file
|
||||
do
|
||||
if [ -f "$file" ]
|
||||
then
|
||||
mv "$file" "$target_directory$(basename -- "${file//,U=[0-9]*:/:}")"
|
||||
fi
|
||||
done < <(notmuch search --output=files --format=text0 tag:"$tag")
|
||||
|
||||
notmuch tag -inbox -"${tag}" +"${tag}"local -- tag:"$tag"
|
||||
done
|
||||
fi
|
||||
|
||||
### PROT 2025-03-01: To archive mail manually, I did the following.
|
||||
### This is useful once every few years, so there is no need to
|
||||
### automate it. Note that this assumes that mbsync can remove and
|
||||
### expunge mail on the far/server side.
|
||||
#
|
||||
# Confirm that we have messages matching these search terms:
|
||||
#
|
||||
# notmuch count tag:"sent" from:*@protesilaos.com date:..2024-01-01
|
||||
#
|
||||
# Now add a tag to all of them to make it clear they are archived:
|
||||
#
|
||||
# notmuch tag +archivesent -sent -- tag:"sent" from:*@protesilaos.com date:..2024-01-01
|
||||
#
|
||||
# Confirm the count is the same for the new tag:
|
||||
#
|
||||
# notmuch count tag:"archivesent"
|
||||
#
|
||||
# Same principle for the inbox:
|
||||
#
|
||||
# notmuch count tag:"inbox" date:..2024-01-01
|
||||
# notmuch tag +archiveinbox -inbox -- tag:"inbox" date:..2024-01-01
|
||||
# notmuch count tag:"archiveinbox"
|
||||
#
|
||||
# Now move the messages to their respective directories, which I
|
||||
# created beforehand (note that I use `echo` to make sure it works
|
||||
# before doing anything---remove the `echo` to actually move the
|
||||
# files).
|
||||
#
|
||||
# while IFS= read -r -d $'\0' file; do echo mv "$file" "$HOME"/.mail/inboxlocal/$(basename -- "${file//,U=[0-9]*:/:}") ; done < <(notmuch search --output=files --format=text0 tag:"archiveinbox" and not tag:"archivesent")
|
||||
#
|
||||
# while IFS= read -r -d $'\0' file; do echo mv "$file" "$HOME"/.mail/sentlocal/$(basename -- "${file//,U=[0-9]*:/:}") ; done < <(notmuch search --output=files --format=text0 tag:"archivesent")
|
||||
#
|
||||
# Then do an `mbsync` followed by `notmuch new` to synchronise everything.
|
||||
#
|
||||
# In case the aforementioned tags are confusing:
|
||||
#
|
||||
# notmuch tag -archiveinbox +iarchive -- tag:archiveinbox
|
||||
# notmuch tag -archivesent +isent -- tag:archivesent
|
||||
|
|
@ -31,9 +31,9 @@ path=/home/prot/.mail
|
|||
#
|
||||
|
||||
[user]
|
||||
name=Protesilaos Stavrou
|
||||
primary_email=public@protesilaos.com
|
||||
other_email=info@protesilaos.com
|
||||
name=Protesilaos
|
||||
primary_email=prot@protesilaos.com
|
||||
other_email=public@protesilaos.com;info@protesilaos.com
|
||||
|
||||
# Configuration for "notmuch new"
|
||||
#
|
||||
|
|
@ -65,7 +65,7 @@ ignore=.uidvalidity;.mbsyncstate;.mbsyncstate.new;.mbsyncstate.lock
|
|||
#
|
||||
|
||||
[search]
|
||||
exclude_tags=del;spam;
|
||||
exclude_tags=
|
||||
|
||||
# Maildir compatibility configuration
|
||||
#
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Redshift
|
||||
Exec=redshift-gtk
|
||||
Icon=redshift
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
StartupNotify=true
|
||||
Hidden=false
|
||||
X-GNOME-Autostart-enabled=true
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* rofi -dump-theme output.
|
||||
* Rofi version: 1.6.1
|
||||
* User: Protesilaos Stavrou
|
||||
* Copyright: Protesilaos Stavrou --- Released under the GNU GPLv3
|
||||
* User: Protesilaos
|
||||
* Copyright: Protesilaos --- Released under the GNU GPLv3
|
||||
* Comment: Simple port of the Modus Operandi theme for Emacs (https://protesilaos.com/modus-themes)
|
||||
**/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* rofi -dump-theme output.
|
||||
* Rofi version: 1.6.1
|
||||
* User: Protesilaos Stavrou
|
||||
* Copyright: Protesilaos Stavrou --- Released under the GNU GPLv3
|
||||
* User: Protesilaos
|
||||
* Copyright: Protesilaos --- Released under the GNU GPLv3
|
||||
* Comment: Simple port of the Modus themes for Emacs (https://protesilaos.com/modus-themes)
|
||||
**/
|
||||
|
||||
|
|
@ -11,8 +11,8 @@ window {
|
|||
border: 5px;
|
||||
border-color: @bg-alt;
|
||||
padding: 10px;
|
||||
width: 25%;
|
||||
height: 15%;
|
||||
width: 33%;
|
||||
height: 8%;
|
||||
/* max-height: 25%; */
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/**
|
||||
* rofi -dump-theme output.
|
||||
* Rofi version: 1.6.1
|
||||
* User: Protesilaos Stavrou
|
||||
* Copyright: Protesilaos Stavrou --- Released under the GNU GPLv3
|
||||
* User: Protesilaos
|
||||
* Copyright: Protesilaos --- Released under the GNU GPLv3
|
||||
* Comment: Simple port of the Modus Operandi theme for Emacs (https://protesilaos.com/modus-themes)
|
||||
**/
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
# guidelines), refer to my Tempus themes (partially incorporated in my
|
||||
# dotfiles as well): https://gitlab.com/protesilaos/tempus-themes.
|
||||
#
|
||||
# Copyright (c) 2019 Protesilaos Stavrou <info@protesilaos.com>
|
||||
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Delight (DE light) --- Toggle DE/WM and Emacs themes
|
||||
#
|
||||
# Copyright (c) 2020-2023 Protesilaos Stavrou <info@protesilaos.com>
|
||||
# Copyright (C) 2020-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -29,10 +29,12 @@
|
|||
|
||||
#### General
|
||||
|
||||
gtk_theme_light="adw-gtk3"
|
||||
gtk_theme_dark="adw-gtk3-dark"
|
||||
icon_theme_light="Papirus"
|
||||
icon_theme_dark="Papirus-Dark"
|
||||
gtk_theme_light="Adwaita"
|
||||
gtk_theme_dark="Adwaita-dark"
|
||||
# icon_theme_light="Papirus-Light"
|
||||
# icon_theme_dark="Papirus-Dark"
|
||||
icon_theme_light="Adwaita"
|
||||
icon_theme_dark="Adwaita"
|
||||
|
||||
_depcheck ()
|
||||
{
|
||||
|
|
@ -55,20 +57,14 @@ _emacs ()
|
|||
|
||||
pgrep -x emacs > /dev/null || return 1
|
||||
|
||||
local light_theme
|
||||
local dark_theme
|
||||
|
||||
light_theme="modus-operandi"
|
||||
dark_theme="modus-vivendi"
|
||||
|
||||
# Just switch to the appropriate theme for Emacs:
|
||||
# https://git.sr.ht/~protesilaos/modus-themes
|
||||
# https://github.com/protesilaos/modus-themes
|
||||
case "$1"
|
||||
in
|
||||
# TODO 2023-03-01: Define a function to load appropriate Emacs
|
||||
# theme.
|
||||
l*) emacsclient -e "(when (and (functionp 'modus-themes--current-theme) (modus-themes--current-theme)) (modus-themes-load-theme '${light_theme}))" > /dev/null ;;
|
||||
d*) emacsclient -e "(when (and (functionp 'modus-themes--current-theme) (modus-themes--current-theme)) (modus-themes-load-theme '${dark_theme}))" > /dev/null ;;
|
||||
l*) emacsclient -e "(when (and (functionp 'modus-themes-get-themes) (modus-themes-get-themes)) (modus-themes-load-random-light))" > /dev/null ;;
|
||||
d*) emacsclient -e "(when (and (functionp 'modus-themes-get-themes) (modus-themes-get-themes)) (modus-themes-load-random-dark))" > /dev/null ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +78,7 @@ _xterm ()
|
|||
[ -f "$xresources" ] || { echo "No Xresources file"; exit 1; }
|
||||
[ -f "$active_theme" ] || { echo "No active Xresources theme"; exit 1; }
|
||||
|
||||
case ${1:-$wm_theme}
|
||||
case $wm_theme
|
||||
in
|
||||
light) _sed "s,vivendi,operandi," "$active_theme" ;;
|
||||
dark) _sed "s,operandi,vivendi," "$active_theme" ;;
|
||||
|
|
@ -92,7 +88,30 @@ _xterm ()
|
|||
|
||||
# Another one of my scripts for live recolouring running terminal
|
||||
# emulators, by using escape sequences.
|
||||
repaint_terminals &
|
||||
repaint_terminals.sh &
|
||||
}
|
||||
|
||||
_xfce4terminal ()
|
||||
{
|
||||
_depcheck xfce4-terminal
|
||||
|
||||
active_theme="$HOME"/.config/xfce4/terminal/terminalrc
|
||||
palette_light="#000000;#a60000;#006800;#6f5500;#0031a9;#8f0075;#005e8b;#aaaaaa;#505050;#972500;#00663f;#884900;#0000b0;#531ab6;#005f5f;#ffffff;"
|
||||
palette_dark="#000000;#ff5f59;#44bc44;#d0bc00;#2fafff;#f78fe7;#00d3d0;#aaaaaa;#505050;#ff6b55;#00c06f;#fec43f;#00bcff;#b6a0ff;#6ae4b9;#ffffff;"
|
||||
|
||||
case "$1"
|
||||
in
|
||||
light)
|
||||
_sed "s,ColorForeground=.*,ColorForeground=#000000," "$active_theme"
|
||||
_sed "s,ColorBackground=.*,ColorBackground=#ffffff," "$active_theme"
|
||||
_sed "s,ColorPalette=.*,ColorPalette=$palette_light," "$active_theme"
|
||||
;;
|
||||
dark)
|
||||
_sed "s,ColorForeground=.*,ColorForeground=#ffffff," "$active_theme"
|
||||
_sed "s,ColorBackground=.*,ColorBackground=#000000," "$active_theme"
|
||||
_sed "s,ColorPalette=.*,ColorPalette=$palette_dark," "$active_theme"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# TODO 2021-08-27: dunst
|
||||
|
|
@ -168,8 +187,8 @@ _xsettingsd ()
|
|||
|
||||
if [ -f "$xsettingsd" ]
|
||||
then
|
||||
_sed "s,\(.*\/ThemeName\) \"[a-zA-Z-]*\",\1 \"${1}\"," "$xsettingsd"
|
||||
_sed "s,\(.*\/IconThemeName\) \"[a-zA-Z-]*\",\1 \"${2}\"," "$xsettingsd"
|
||||
_sed "s,\(.*\/ThemeName\) \"[0-9a-zA-Z-]*\",\1 \"${1}\"," "$xsettingsd"
|
||||
_sed "s,\(.*\/IconThemeName\) \"[0-9a-zA-Z-]*\",\1 \"${2}\"," "$xsettingsd"
|
||||
fi
|
||||
|
||||
# This is a very lightweight program that simply loads the
|
||||
|
|
@ -208,6 +227,7 @@ _bspwm_or_hlwm ()
|
|||
_emacs "$style" &
|
||||
_xsettingsd "$style" &
|
||||
_xterm &
|
||||
_xfce4terminal "$style" &
|
||||
# # NOTE 2021-09-25: I tried alacritty for a while, but the way it
|
||||
# reads the fonts on Xorg makes the point size larger than expected.
|
||||
# Also, it seems to miss some smaller sizes, so I cannot use it
|
||||
|
|
@ -217,7 +237,7 @@ _bspwm_or_hlwm ()
|
|||
_feh &
|
||||
_rofi &
|
||||
|
||||
if [ -f $focus_mode_status ] && [ "$(sed 1q "$focus_mode_status")" = off ]
|
||||
if [ -f "$focus_mode_status" ] && [ "$(sed 1q "$focus_mode_status")" = off ]
|
||||
then
|
||||
_polybar launch
|
||||
else
|
||||
|
|
@ -276,23 +296,16 @@ _i3 ()
|
|||
|
||||
#### Gsettings
|
||||
|
||||
# TODO 2024-04-05: Remove those functions or, at least, make them easier to remember.
|
||||
|
||||
_gsettings ()
|
||||
{
|
||||
gsettings "$1" "$2" "${@:3}"
|
||||
}
|
||||
|
||||
# Get or set the GTK theme
|
||||
_gsettings_gtk_act ()
|
||||
_gsettings_gtk_act_theme_interface ()
|
||||
{
|
||||
_gsettings "$1" "$2" gtk-theme "${@:3}"
|
||||
gsettings "$1" org.gnome.desktop.interface gtk-theme "${@:2}"
|
||||
}
|
||||
|
||||
# Set the Color Scheme
|
||||
_gsettings_color_scheme_set ()
|
||||
{
|
||||
_gsettings 'set' 'org.gnome.desktop.interface' 'color-scheme' "$1"
|
||||
gsettings set org.gnome.desktop.interface color-scheme "$1"
|
||||
}
|
||||
|
||||
##### Gnome
|
||||
|
|
@ -300,22 +313,21 @@ _gsettings_color_scheme_set ()
|
|||
_gnome_theme ()
|
||||
{
|
||||
_depcheck gsettings
|
||||
interface="org.gnome.desktop.interface"
|
||||
|
||||
if [ "$(_gsettings_gtk_act 'get' $interface)" = "$gtk_theme_light" ]
|
||||
if [ "$(_gsettings_gtk_act_theme_interface get)" = "'$gtk_theme_light'" ]
|
||||
then
|
||||
_gsettings_gtk_act 'set' $interface "$gtk_theme_dark"
|
||||
_gsettings_gtk_act_theme_interface set "$gtk_theme_dark"
|
||||
else
|
||||
_gsettings_gtk_act 'set' $interface "$gtk_theme_light"
|
||||
_gsettings_gtk_act_theme_interface set "$gtk_theme_light"
|
||||
fi
|
||||
|
||||
if [ "$(gsettings get org.gnome.desktop.interface color-scheme)" = "'prefer-light'" ]
|
||||
then
|
||||
style=dark
|
||||
_gsettings_color_scheme_set 'prefer-dark'
|
||||
_gsettings_color_scheme_set prefer-dark
|
||||
else
|
||||
style=light
|
||||
_gsettings_color_scheme_set 'prefer-light'
|
||||
_gsettings_color_scheme_set prefer-light
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -340,26 +352,26 @@ _xfquery_set ()
|
|||
_xfce ()
|
||||
{
|
||||
_depcheck gsettings
|
||||
interface="org.gnome.desktop.interface"
|
||||
|
||||
if [ "$(_xfquery_get 'xsettings' '/Net/ThemeName')" = "$gtk_theme_light" ]
|
||||
then
|
||||
style=dark
|
||||
# _xfquery_set 'xfwm4' '/general/theme' 'adwaita-dark'
|
||||
_gsettings_gtk_act 'set' $interface "$gtk_theme_dark"
|
||||
_gsettings_gtk_act_theme_interface set "$gtk_theme_dark"
|
||||
_xfquery_set 'xsettings' '/Net/ThemeName' "$gtk_theme_dark"
|
||||
_xfquery_set 'xsettings' '/Net/IconThemeName' "$icon_theme_dark"
|
||||
_gsettings_color_scheme_set 'prefer-dark'
|
||||
else
|
||||
style=light
|
||||
# _xfquery_set 'xfwm4' '/general/theme' 'adwaita'
|
||||
_gsettings_gtk_act 'set' $interface "$gtk_theme_light"
|
||||
_gsettings_gtk_act_theme_interface set "$gtk_theme_light"
|
||||
_xfquery_set 'xsettings' '/Net/ThemeName' "$gtk_theme_light"
|
||||
_xfquery_set 'xsettings' '/Net/IconThemeName' "$icon_theme_light"
|
||||
_gsettings_color_scheme_set 'prefer-light'
|
||||
fi
|
||||
|
||||
_emacs "$style"
|
||||
_emacs "$style" &
|
||||
_xfce4terminal "$style" &
|
||||
}
|
||||
|
||||
#### Sessions
|
||||
|
|
@ -368,13 +380,13 @@ case "$DESKTOP_SESSION"
|
|||
in
|
||||
bspwm)
|
||||
wm_executable=bspwm
|
||||
conf_colors=bspwm_conf_colors
|
||||
conf_colors=bspwm_conf_colors.sh
|
||||
|
||||
_bspwm_or_hlwm
|
||||
;;
|
||||
herbstluftwm)
|
||||
wm_executable=herbstluftwm
|
||||
conf_colors=herbstluftwm_conf_colors
|
||||
conf_colors=herbstluftwm_conf_colors.sh
|
||||
|
||||
_bspwm_or_hlwm
|
||||
;;
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# pcb --- Print the terminal's active colours.
|
||||
#
|
||||
# Copyright (c) 2019-2023 Protesilaos Stavrou <info@protesilaos.com>
|
||||
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# poweroptionsmenu --- rofi or dmenu interface for session actions.
|
||||
#
|
||||
# Copyright (c) 2019-2023 Protesilaos Stavrou <info@protesilaos.com>
|
||||
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -27,7 +27,7 @@ _pkill() {
|
|||
}
|
||||
|
||||
_kill_processes() {
|
||||
_pkill bspwm_external_rules
|
||||
_pkill bspwm_external_rules.sh
|
||||
_pkill sxhkd
|
||||
_pkill polybar
|
||||
_pkill feh
|
||||
|
|
@ -37,9 +37,7 @@ _kill_processes() {
|
|||
# In case something stops working, we can call this script
|
||||
# (Ctrl+Alt+Delete on my sxhkdrc) and apply the modifications again.
|
||||
_kbd_reset() {
|
||||
setxkbmap -layout 'us,gr' -option '' -option 'ctrl:nocaps' \
|
||||
-option 'altwin:menu_win' -option 'caps:none' \
|
||||
-option 'compose:ins' -option 'grp:win_space_toggle'
|
||||
setxkbmap -layout 'us,gr' -option '' -option 'grp:win_space_toggle' -option 'compose:menu'
|
||||
}
|
||||
|
||||
_check_systemd() {
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# tmr --- Bash script to run a simple timer
|
||||
#
|
||||
# Copyright (c) 2019-2023 Protesilaos Stavrou <info@protesilaos.com>
|
||||
# Copyright (C) 2019-2026 Protesilaos <info@protesilaos.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -18,14 +18,8 @@ _checkexec ()
|
|||
|
||||
### General settings
|
||||
|
||||
# Include my scripts in the PATH.
|
||||
if [ -d "$HOME"/bin ]
|
||||
then
|
||||
PATH=$PATH:"$HOME"/bin
|
||||
fi
|
||||
|
||||
if [ -d "$HOME"/.local/bin ]
|
||||
then
|
||||
# Set PATH so it includes user's private executables.
|
||||
if [ -d "$HOME"/.local/bin ]; then
|
||||
PATH=$PATH:"$HOME"/.local/bin
|
||||
fi
|
||||
|
||||
|
|
@ -34,11 +28,6 @@ then
|
|||
PATH=$PATH:"$HOME"/Builds/bin
|
||||
fi
|
||||
|
||||
if [ -d /opt/bin ]
|
||||
then
|
||||
PATH=$PATH:/opt/bin
|
||||
fi
|
||||
|
||||
if [ -d "/var/lib/flatpak/exports/bin" ]
|
||||
then
|
||||
PATH=$PATH:"/var/lib/flatpak/exports/bin"
|
||||
|
|
@ -65,8 +54,8 @@ export SSH_AUTH_SOCK="/run/user/$(id -u)/gnupg/S.gpg-agent.ssh"
|
|||
# Default editor.
|
||||
if pgrep -x emacs > /dev/null
|
||||
then
|
||||
export VISUAL="emacsclient -c"
|
||||
export EDITOR="emacsclient -t"
|
||||
export EDITOR="emacsclient -a emacs -t -r"
|
||||
export VISUAL="emacsclient -a emacs -t -r"
|
||||
elif _checkexec gvim
|
||||
then
|
||||
export VISUAL="gvim"
|
||||
|
|
@ -82,7 +71,7 @@ fi
|
|||
# The COLORTERM is documented in (info "(emacs) General Variables").
|
||||
# I found the reference to `dumb-emacs-ansi' in (info "(emacs)
|
||||
# Connection Variables").
|
||||
if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ]
|
||||
if [ "$TERM" = "dumb" ] && [ "$INSIDE_EMACS" ] || [ "$TERM" = "dumb-emacs-ansi" ] && [ "$INSIDE_EMACS" ]
|
||||
then
|
||||
PAGER="cat"
|
||||
alias less="cat"
|
||||
|
|
@ -163,6 +152,17 @@ _checkexec lesspipe && eval "$(SHELL=/bin/sh lesspipe)"
|
|||
# remember the original command when necessary. There are some
|
||||
# exceptions for commands I seldom execute.
|
||||
|
||||
#### Apt (Debian)
|
||||
|
||||
if _checkexec apt
|
||||
then
|
||||
alias aptuu="sudo apt update && sudo apt upgrade"
|
||||
alias aptar="sudo apt autoremove"
|
||||
alias aptss="apt search -n"
|
||||
alias aptsh="apt show"
|
||||
alias aptls="dpkg -L"
|
||||
fi
|
||||
|
||||
#### Pacman and Yay (Arch Linux)
|
||||
|
||||
if _checkexec pacman
|
||||
|
|
@ -274,7 +274,7 @@ fi
|
|||
if _checkexec yt-dlp
|
||||
then
|
||||
alias ytaud='yt-dlp --add-metadata -ci --extract-audio --audio-format mp3 -o "%(title)s.%(ext)s"'
|
||||
alias ytvid='yt-dlp --add-metadata --no-playlist --no-part --write-description --newline --prefer-free-formats -o "%(title)s.%(ext)s" '
|
||||
alias ytvid='yt-dlp --add-metadata --no-playlist --no-part --write-description --newline --prefer-free-formats -o "%(title)s.%(ext)s"'
|
||||
fi
|
||||
|
||||
# Just for fun.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
# Configuration file for dircolors, a utility to help you set the
|
||||
# LS_COLORS environment variable used by GNU ls with the --color option.
|
||||
# Copyright (C) 1996-2023 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2026 Free Software Foundation, Inc.
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted provided the copyright notice and this notice are preserved.
|
||||
# The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
Socket-activated dirmngr and gpg-agent with systemd
|
||||
===================================================
|
||||
|
||||
When used on a GNU/Linux system supervised by systemd, you can ensure
|
||||
that the GnuPG daemons dirmngr and gpg-agent are launched
|
||||
automatically the first time they're needed, and shut down cleanly at
|
||||
session logout. This is done by enabling user services via
|
||||
socket-activation.
|
||||
|
||||
System distributors
|
||||
-------------------
|
||||
|
||||
The *.service and *.socket files (from this directory) should be
|
||||
placed in /usr/lib/systemd/user/ alongside other user-session services
|
||||
and sockets.
|
||||
|
||||
To enable socket-activated dirmngr for all accounts on the system,
|
||||
use:
|
||||
|
||||
systemctl --user --global enable dirmngr.socket
|
||||
|
||||
To enable socket-activated gpg-agent for all accounts on the system,
|
||||
use:
|
||||
|
||||
systemctl --user --global enable gpg-agent.socket
|
||||
|
||||
Additionally, you can enable socket-activated gpg-agent ssh-agent
|
||||
emulation for all accounts on the system with:
|
||||
|
||||
systemctl --user --global enable gpg-agent-ssh.socket
|
||||
|
||||
You can also enable restricted ("--extra-socket"-style) gpg-agent
|
||||
sockets for all accounts on the system with:
|
||||
|
||||
systemctl --user --global enable gpg-agent-extra.socket
|
||||
|
||||
Individual users
|
||||
----------------
|
||||
|
||||
A user on a system with systemd where this has not been installed
|
||||
system-wide can place these files in ~/.config/systemd/user/ to make
|
||||
them available.
|
||||
|
||||
If a given service isn't installed system-wide, or if it's installed
|
||||
system-wide but not globally enabled, individual users will still need
|
||||
to enable them. For example, to enable socket-activated dirmngr for
|
||||
all future sessions:
|
||||
|
||||
systemctl --user enable dirmngr.socket
|
||||
|
||||
To enable socket-activated gpg-agent with ssh support, do:
|
||||
|
||||
systemctl --user enable gpg-agent.socket gpg-agent-ssh.socket
|
||||
|
||||
These changes won't take effect until your next login after you've
|
||||
fully logged out (be sure to terminate any running daemons before
|
||||
logging out).
|
||||
|
||||
If you'd rather try a socket-activated GnuPG daemon in an
|
||||
already-running session without logging out (with or without enabling
|
||||
it for all future sessions), kill any existing daemon and start the
|
||||
user socket directly. For example, to set up socket-activated dirmgnr
|
||||
in the current session:
|
||||
|
||||
gpgconf --kill dirmngr
|
||||
systemctl --user start dirmngr.socket
|
||||
|
|
@ -1 +0,0 @@
|
|||
/home/prot/.config/systemd/user/dirmngr.socket
|
||||
|
|
@ -1 +0,0 @@
|
|||
/home/prot/.config/systemd/user/gpg-agent-browser.socket
|
||||
|
|
@ -1 +0,0 @@
|
|||
/home/prot/.config/systemd/user/gpg-agent-extra.socket
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue