Compare commits
838 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb630b2de0 | |||
| 548de98551 | |||
| 49f2bae9e1 | |||
| 5ca858d112 | |||
| 1bb433dc64 | |||
| aec17d1077 | |||
| 049b517b88 | |||
| 35272ce1ac | |||
|
|
0036932693 | ||
| e676c095c2 | |||
| ef4c925e37 | |||
| 80d72a29bb | |||
| 0854b5a769 | |||
| 2ca99fe831 | |||
| 8793a733a6 | |||
| 6827940466 | |||
| fde615ff3f | |||
| d002c82271 | |||
| f64229b52c | |||
|
|
3d5766ac33 | ||
| 60edfc00ee | |||
| 6cd294373c | |||
| 02c89b99dd | |||
| fe4e972fbe | |||
| e4a9e06d2a | |||
| 487b5fa4f8 | |||
|
|
946e8e7228 | ||
| 04206b6a14 | |||
| 9ea2a2f332 | |||
|
|
364cb1ca2e | ||
|
|
2ba878713c | ||
| 2678ec723e | |||
| dbf2d80863 | |||
| fab98bc613 | |||
|
|
7b16cde540 | ||
| 4150001d8b | |||
| 32ed0d2348 | |||
| 8731b1a7d1 | |||
| 4743222044 | |||
| 14a600f922 | |||
| 13e6d180f8 | |||
|
|
836f941e16 | ||
| a02cd1d7ef | |||
| c969f5ed15 | |||
|
|
36ce05e85a | ||
| ac7c97442e | |||
| 7fc3f4a569 | |||
| dbb45a1947 | |||
| 3da150bb64 | |||
| 46968e632b | |||
|
|
1e01580d8f | ||
| edbc758c67 | |||
|
|
824f5bd06d | ||
| a7ba2fdf1a | |||
| e6b88816fd | |||
| db851cf4f8 | |||
| fc6a9a33e1 | |||
| 0582fbd30c | |||
| 137efe5c62 | |||
| 72324c27de | |||
| ea233facc8 | |||
| 19feda919e | |||
| e4df61f020 | |||
| 54b85b1c99 | |||
| ddf5117fd9 | |||
| 5b46158008 | |||
| a41a5504f4 | |||
|
|
35a8d0eaa4 | ||
| 8a1614ab59 | |||
| cc43cb2d6e | |||
| 1ba0dd183c | |||
| 44d63a398a | |||
| 92448eec67 | |||
| 4e882b376b | |||
| 5f8d942f0a | |||
| 41e5e3f789 | |||
| 826b0789cb | |||
| bfa3b55542 | |||
| f7e66bb957 | |||
| d7ab887234 | |||
|
|
b24fc4a422 | ||
| 0e4b95d9b9 | |||
| 38d036d96f | |||
| 4c61c551aa | |||
| ff6e08902f | |||
| bbd5637e9b | |||
|
|
d46c079afb | ||
| 42ab856e45 | |||
|
|
478e3020b1 | ||
| a20123fc24 | |||
| fb9d30520a | |||
| 521298733e | |||
| f8a391e24f | |||
|
|
aa376f9b14 | ||
| 16b39fe157 | |||
| 40c3cecabb | |||
| ce99209f0d | |||
| d1dbb84c16 | |||
| 5b84718021 | |||
| 3d727e6411 | |||
| 7d487bfc98 | |||
| 11fc1edd94 | |||
| b416264138 | |||
| 11323a70aa | |||
| db32fd7a87 | |||
| 017702ea0d | |||
| 30830ed6ed | |||
| 7932fa677f | |||
| c1b78a8ea7 | |||
| 3128ef7934 | |||
| c3394f7046 | |||
| b1f957e98f | |||
| f788c9374c | |||
| b7602b0bbe | |||
| 7b84faabf9 | |||
| 8fdae6c1d7 | |||
| 190ed5b2dc | |||
| 9d5f5abef4 | |||
| a0513c0a05 | |||
| 1d64a57e24 | |||
|
|
1a4585aa7a | ||
| 60fd4c3b4c | |||
| 29e14c1aee | |||
|
|
ee1ed077b9 | ||
| 78eb657897 | |||
| a5b6fc8eda | |||
| 4421869b85 | |||
| 6d07d8db53 | |||
| 483e5f6dbe | |||
| 83492e4e69 | |||
| 4fcd310048 | |||
| db8e87e5dd | |||
| ba04fb2af7 | |||
| caff604d95 | |||
| 2b9059f726 | |||
| 00c967d7c1 | |||
|
|
ab26b82a9f | ||
| f540fc2c04 | |||
| c6c81ddece | |||
| 3eae0f07be | |||
| cbc5e81c9d | |||
|
|
6729ecddea | ||
| 485306fbe2 | |||
| 480d4de750 | |||
| d7dde44a0c | |||
| 237b306eb8 | |||
| bd01b478d5 | |||
| a37f8e3b30 | |||
| 9c6d6c4f31 | |||
| 2dbd201e1e | |||
| 3dd834fcdf | |||
| 659d1e0c4e | |||
|
|
0379e302ac | ||
| d577b3c958 | |||
| 3d0fffd49a | |||
| 013fa30658 | |||
|
|
4e37e8ca47 | ||
| 26c8f28034 | |||
| 6be65c04a6 | |||
| 7bfd7b12c4 | |||
| d48a66e393 | |||
| d873f04f70 | |||
| 7961a441ad | |||
|
|
c55cd345ba | ||
| 9e1ec1641c | |||
| 2911257eb9 | |||
| f2ee600c28 | |||
| 61914f8dc7 | |||
| 6dbb39d404 | |||
| 890f10e176 | |||
| 6b27d14f45 | |||
| efc69ab769 | |||
| cdd07a4981 | |||
| e2cf417144 | |||
| f97a04179d | |||
| 2f6ab5fd92 | |||
| 088e326bd1 | |||
|
|
c87fbe2b99 | ||
| 5bb6ba4aee | |||
| 8d20ad9eed | |||
| 71d23a4727 | |||
| 6530d48b00 | |||
| d99e056a76 | |||
| 109b5785af | |||
| a6bcfdfca9 | |||
| 4e023703b6 | |||
| 5814462f5d | |||
| 354f257c03 | |||
|
|
a2f0ff3e09 | ||
| 07a7a75b46 | |||
| 887260cd8a | |||
| ef5363cb1f | |||
| 246fc4d7ee | |||
| 91ec0cf4a4 | |||
| 2a80795331 | |||
| 72d6b0a4a0 | |||
| 4eea793e69 | |||
| b3410e85bc | |||
| c12cb82fb5 | |||
|
|
68a37d8c79 | ||
| f107d1df71 | |||
| 61f6337f5f | |||
| 0f33f72de7 | |||
| c2db9e65fa | |||
| 97188e73a5 | |||
| 3cedff273a | |||
|
|
a1ce4389e8 | ||
| 35e2a54172 | |||
|
|
7234111d6d | ||
| a105b8e38d | |||
| 5dc3b7d337 | |||
| d5dffabcb2 | |||
| 5ea23d58d1 | |||
| 1a2d29aee7 | |||
| b46bd0059e | |||
| 78c95de784 | |||
| e4d87afab3 | |||
| 70594f4db2 | |||
| f661db158b | |||
| 787d6c082f | |||
| ad94be925c | |||
| fc82d4f5eb | |||
| e5eefb1d2d | |||
| 6525fa774e | |||
| 514cbe25c7 | |||
| 84dd939bbf | |||
| f94b8ae59b | |||
| 7b3dc951c9 | |||
| 0a49e1a4ef | |||
|
|
6ca36847f1 | ||
|
|
5ca728ba06 | ||
| 813354b662 | |||
|
|
8d40179a73 | ||
|
|
f7e725b5b9 | ||
| 4ee160fb66 | |||
|
|
1024f93446 | ||
|
|
9c8d7692bc | ||
|
|
9131c72501 | ||
|
|
26ff6c9a46 | ||
|
|
604dedb36d | ||
|
|
395f3c0742 | ||
|
|
0c44531a7f | ||
|
|
4e850bb322 | ||
|
|
09d04a7850 | ||
|
|
4df37b4341 | ||
|
|
5e2b3367de | ||
|
|
b6aecb12f6 | ||
|
|
3f9b230381 | ||
|
|
b395b70ecd | ||
|
|
06102ebdae | ||
|
|
bd4e0df7b3 | ||
|
|
ebec585690 | ||
|
|
ff4609e432 | ||
|
|
ef3e7d92bc | ||
|
|
d6338fc021 | ||
|
|
3eaeb6a5da | ||
|
|
8b547f2af0 | ||
|
|
1febe66f84 | ||
|
|
6ffcc498ac | ||
|
|
fe47c6c7a1 | ||
|
|
0d32a10ca3 | ||
|
|
cc6e5d01dd | ||
|
|
068a5b5149 | ||
|
|
117a4580b4 | ||
|
|
0db7f96fd7 | ||
|
|
300248de54 | ||
|
|
3ee2b03330 | ||
|
|
3bec2c91a8 | ||
|
|
b4ca5bfda3 | ||
|
|
308e257872 | ||
|
|
7db2a2c826 | ||
|
|
22f6ea8a3e | ||
|
|
91596fa81c | ||
|
|
e5c8e5eaff | ||
|
|
8647dc57a1 | ||
|
|
8f41005da7 | ||
|
|
7108523a4c | ||
|
|
ee34ccdbc3 | ||
|
|
959030f70d | ||
|
|
59cec0ffa5 | ||
|
|
1eed4cc7ee | ||
|
|
c13889592f | ||
|
|
2e192be2e1 | ||
|
|
c96605e79c | ||
|
|
a1ce2ecba6 | ||
|
|
a1410ae7ad | ||
|
|
20870b9004 | ||
|
|
3dcd99563a | ||
|
|
2337f9ab60 | ||
|
|
6ce4fb3973 | ||
|
|
387d7d2243 | ||
|
|
7de835fab4 | ||
|
|
3e0a2d630b | ||
|
|
09a8b41063 | ||
|
|
349380ae6b | ||
|
|
f7806f5479 | ||
|
|
c8c30e9861 | ||
|
|
083c266438 | ||
|
|
dd9bdca5e0 | ||
|
|
9902560061 | ||
|
|
dd9aa4b6ea | ||
|
|
be8bdef9c8 | ||
|
|
e185a32685 | ||
|
|
2b480e50e6 | ||
|
|
b65acca903 | ||
|
|
08e2951513 | ||
|
|
83a2ce38ea | ||
|
|
60dd5f598a | ||
|
|
6eea5bce47 | ||
|
|
a106f4aea0 | ||
|
|
90ed12d551 | ||
|
|
e0694c8862 | ||
|
|
d27ce26be0 | ||
|
|
587d08773f | ||
|
|
362bb14d7e | ||
|
|
153a89b188 | ||
|
|
453ba9f8ad | ||
|
|
3e807f5235 | ||
|
|
7e57b3de7d | ||
|
|
226605a996 | ||
|
|
1b4b41846f | ||
|
|
3196f0dcb9 | ||
|
|
3f745e7e4d | ||
|
|
f9a2a981b4 | ||
|
|
1339dc4a2f | ||
|
|
b4a5746093 | ||
|
|
2faaf4e045 | ||
|
|
3990d370fa | ||
|
|
b28a2c24c1 | ||
|
|
e3c25586e4 | ||
|
|
9d385efbfc | ||
|
|
d99c24842f | ||
|
|
61271dc70c | ||
|
|
8697344f98 | ||
|
|
d65b574cb2 | ||
|
|
d808534da6 | ||
|
|
a4b358fd9c | ||
|
|
8f6f27e647 | ||
|
|
f888aabc80 | ||
|
|
2572d0ea9e | ||
|
|
d2f16dfbbf | ||
|
|
392cb66382 | ||
|
|
0b07b93de2 | ||
|
|
8b6506b36c | ||
|
|
ab8b9c8376 | ||
|
|
5dd2af7250 | ||
|
|
ac21109307 | ||
|
|
cb88db8cfc | ||
|
|
960909b81d | ||
|
|
dc933934eb | ||
|
|
827c25fb57 | ||
|
|
3374dfd28f | ||
|
|
9a87d76969 | ||
|
|
200fe1054f | ||
|
|
52a58b14c3 | ||
|
|
15035c02cb | ||
|
|
dd678a4a75 | ||
|
|
0a01ed862c | ||
|
|
200babf831 | ||
|
|
d6bc8b51de | ||
|
|
86700c0a7b | ||
|
|
30b07bc98b | ||
|
|
db437905e0 | ||
|
|
c890b86e08 | ||
|
|
a8406657f9 | ||
|
|
4178b78341 | ||
|
|
c4eb3ad368 | ||
|
|
0642ddda36 | ||
|
|
b210378380 | ||
|
|
03cb9c06f3 | ||
|
|
c929c7602f | ||
|
|
e678ef66d8 | ||
|
|
d053b58204 | ||
|
|
eb3123405a | ||
|
|
f12f88ed00 | ||
|
|
3bad452f95 | ||
|
|
2790982ad1 | ||
|
|
4e5f8b5c80 | ||
|
|
4a4c4d8c74 | ||
|
|
bcf02a6b0e | ||
|
|
984b8a4308 | ||
|
|
3db6334445 | ||
|
|
9e596dc271 | ||
|
|
f003249632 | ||
|
|
1714aeac36 | ||
|
|
bca89197ad | ||
|
|
27ac60b856 | ||
|
|
dbb27f7739 | ||
|
|
03a57fce5b | ||
|
|
abfbeb5f84 | ||
|
|
3194add3dc | ||
|
|
1ba88fea88 | ||
|
|
1274811739 | ||
|
|
0af36db6ff | ||
|
|
1a4008fcfb | ||
|
|
57d7527356 | ||
|
|
3d525aa7da | ||
|
|
37e1b12b81 | ||
|
|
abfd4e5196 | ||
|
|
bd35e3b871 | ||
|
|
86aa40af64 | ||
|
|
a89d3e59d6 | ||
|
|
1458ecdcab | ||
|
|
6ccb9ebeff | ||
|
|
e34ab5314a | ||
|
|
0ae31123b9 | ||
|
|
7a17d7931f | ||
|
|
d4084a82ba | ||
|
|
9d69b63a0f | ||
|
|
b83c09a2b3 | ||
|
|
bee8d6e9e6 | ||
|
|
48cb62282d | ||
|
|
1e48a3e03d | ||
|
|
4e8aa7ed1d | ||
|
|
919a52a630 | ||
|
|
b0c950a96a | ||
|
|
c4438579f6 | ||
|
|
59e81d3996 | ||
|
|
7f161e6683 | ||
|
|
ded0be275f | ||
|
|
e3fdf84f5c | ||
|
|
194e4e01b7 | ||
|
|
e5c9b85008 | ||
|
|
e044aeb5ed | ||
|
|
0a2046e985 | ||
|
|
626b036b4b | ||
|
|
ce625aca0c | ||
|
|
1715a1f626 | ||
|
|
f002eca258 | ||
|
|
2814f44163 | ||
|
|
ea7517be05 | ||
|
|
508fd5f8a2 | ||
|
|
c95ff42dee | ||
|
|
c342f4f6f5 | ||
|
|
393fe3962f | ||
|
|
8ae22127c0 | ||
|
|
8f942f6f60 | ||
|
|
b712efd278 | ||
|
|
ee4d97ff9a | ||
|
|
9ce861b080 | ||
|
|
69d38d7048 | ||
|
|
0fc5f457ad | ||
|
|
64bf7024d2 | ||
|
|
0ebd8d9035 | ||
|
|
52caedc1f1 | ||
|
|
34ae8ade7b | ||
|
|
9ff6e5879f | ||
|
|
20fae8a870 | ||
|
|
eff942433d | ||
|
|
61c9e12a4a | ||
|
|
0aba60b5de | ||
|
|
62a6cc56b9 | ||
|
|
e5d841a0a7 | ||
|
|
3e50e608c1 | ||
|
|
76f100ca77 | ||
|
|
0cad0e7e66 | ||
|
|
2fb481c4cb | ||
|
|
6ea25a22b3 | ||
|
|
577f00dfef | ||
|
|
722bd1cd80 | ||
|
|
1ba314d961 | ||
|
|
84dce8c537 | ||
|
|
3345c51064 | ||
|
|
a7e450904c | ||
|
|
23efc8e54d | ||
|
|
1d4c8a7645 | ||
|
|
7272e035d9 | ||
|
|
8a215ad742 | ||
|
|
6e69ee20f6 | ||
|
|
bc15da2f1a | ||
|
|
dd9e827735 | ||
|
|
3636efe7f8 | ||
|
|
053914ec45 | ||
|
|
1e884e7c89 | ||
|
|
f27d60f827 | ||
|
|
754a63c74b | ||
|
|
2fe888e186 | ||
|
|
0d4143582f | ||
|
|
654ca4b527 | ||
|
|
802f444ba6 | ||
|
|
86a15e6363 | ||
|
|
96c1487bfa | ||
|
|
1e899bf18e | ||
|
|
f03f8c0da1 | ||
|
|
1378a74e87 | ||
|
|
63bc398f1c | ||
|
|
579a0cd0ce | ||
|
|
f433ab8a6f | ||
|
|
eb0a81f7a2 | ||
|
|
24b88a0483 | ||
|
|
f73c6d4aa8 | ||
|
|
925ded6cea | ||
|
|
92d0e195ab | ||
|
|
4da82e872c | ||
|
|
b0ece71de3 | ||
|
|
8b5dc69950 | ||
|
|
20a4cdb505 | ||
|
|
01d937afd8 | ||
|
|
32b421d402 | ||
|
|
0ee47cc6f0 | ||
|
|
b8e0874ab0 | ||
|
|
ae449b89eb | ||
|
|
91222f9d74 | ||
|
|
feb2ea18fb | ||
|
|
726d265cca | ||
|
|
7d9c7ddf18 | ||
|
|
5e505d0a8d | ||
|
|
e5b6a2586b | ||
|
|
c5dd05bb1e | ||
|
|
0113a4e0b4 | ||
|
|
5a2e3e4700 | ||
|
|
b05cb30ed7 | ||
|
|
dc53eef829 | ||
|
|
2f728de6a1 | ||
|
|
4dc5ff60fd | ||
|
|
a414c87e06 | ||
|
|
b3db55708a | ||
|
|
7d7ccb5c9b | ||
|
|
ece5f92307 | ||
|
|
0e4f552be2 | ||
|
|
23902ea25c | ||
|
|
a7f0457dab | ||
|
|
4a4cb3409f | ||
|
|
853dfa369f | ||
|
|
738939e4c9 | ||
|
|
3babf36c43 | ||
|
|
b7478c3c1a | ||
|
|
605574f262 | ||
|
|
9165e0bdf9 | ||
|
|
ec886e17fa | ||
|
|
ba67278fad | ||
|
|
539cf402ca | ||
|
|
8eb5a6710d | ||
|
|
03795e5727 | ||
|
|
7959a8b572 | ||
|
|
5c4335ec6a | ||
|
|
99c9e95eeb | ||
|
|
46ef8ad122 | ||
|
|
cec6287e07 | ||
|
|
4364031b16 | ||
|
|
4ef6c7d5c0 | ||
|
|
764e2372a2 | ||
|
|
fb3cedb161 | ||
|
|
ef6bf24cda | ||
|
|
639bd09d0e | ||
|
|
4b57dc2ed8 | ||
|
|
928b829388 | ||
|
|
7b693908df | ||
|
|
8d8b298053 | ||
|
|
281c443b7b | ||
|
|
3805f13369 | ||
|
|
0b1b5bff2d | ||
|
|
f8f8cd0b59 | ||
|
|
4bb65572e4 | ||
|
|
29c1e29bb7 | ||
|
|
105552ac74 | ||
|
|
f28fd7e6c0 | ||
|
|
f3f49fc398 | ||
|
|
893d72a236 | ||
|
|
0637f2763b | ||
|
|
1cb536b1ac | ||
|
|
6740e012a5 | ||
|
|
b1ad90443e | ||
|
|
76c94bfcce | ||
|
|
0ed365170c | ||
|
|
70724ec015 | ||
|
|
f1dfd59c4c | ||
|
|
a0dff5babf | ||
|
|
058d6ac456 | ||
|
|
9f206ebed0 | ||
|
|
59963add14 | ||
|
|
88fd6f01b9 | ||
|
|
5175367b35 | ||
|
|
83ca8066e6 | ||
|
|
92f279e5cf | ||
|
|
f26ef935dd | ||
|
|
6b9bbb615d | ||
|
|
237bc19f68 | ||
|
|
63e33af34d | ||
|
|
d7f10b0247 | ||
|
|
99461e42d0 | ||
|
|
b0e4cf6ed1 | ||
|
|
fca15886aa | ||
|
|
e7ac7ce843 | ||
|
|
c9f9ce07e8 | ||
|
|
e7a31c5394 | ||
|
|
b61a36d675 | ||
|
|
284248fc08 | ||
|
|
7bde025c8e | ||
|
|
e8d0ea12cc | ||
|
|
c175bb4d8a | ||
|
|
b3981b5a4e | ||
|
|
065d752b82 | ||
|
|
062f40e9e5 | ||
|
|
44e2a458f5 | ||
|
|
3b6284bc7c | ||
|
|
52379dbdd7 | ||
|
|
36cd4d4123 | ||
|
|
d355d02b93 | ||
|
|
a3abd4bc91 | ||
|
|
ad6d3193ff | ||
|
|
1040872169 | ||
|
|
7516956aac | ||
|
|
babb870383 | ||
|
|
8dcd57309f | ||
|
|
f292245c23 | ||
|
|
aaeecceabf | ||
|
|
3cdd21a4fa | ||
|
|
496bf616c6 | ||
|
|
df6a2bb0c7 | ||
|
|
3165552c21 | ||
|
|
f26931d4cb | ||
|
|
994daba078 | ||
|
|
311d7d56bd | ||
|
|
d9d2152e04 | ||
|
|
b221921ab7 | ||
|
|
0b5bbcc73c | ||
|
|
21b093559a | ||
|
|
e5566f672a | ||
|
|
715a67b882 | ||
|
|
18abb66ad4 | ||
|
|
9d390c87cf | ||
|
|
e3a0195eb3 | ||
|
|
06221e06cd | ||
|
|
fd513b6ae3 | ||
|
|
1b0ae5b1e3 | ||
|
|
15caa6e449 | ||
|
|
d448ef07e0 | ||
|
|
d7e78514de | ||
|
|
186d620311 | ||
|
|
15cf5bb6f3 | ||
|
|
a74c9a4200 | ||
|
|
0bf4072355 | ||
|
|
a77b997942 | ||
|
|
dc608436c1 | ||
|
|
18c115325a | ||
|
|
bd3628332e | ||
|
|
e75c60ff49 | ||
|
|
165cedf328 | ||
|
|
4c371d8095 | ||
|
|
c5f5a83990 | ||
|
|
d1b7515722 | ||
|
|
b468727a32 | ||
|
|
62d8725d99 | ||
|
|
2a0cd1464a | ||
|
|
f05ab921d7 | ||
|
|
cb3d0a2e8e | ||
|
|
209abfac77 | ||
|
|
5e9ea9fead | ||
|
|
11f193b394 | ||
|
|
4cc49d45c7 | ||
|
|
afe579f5be | ||
|
|
5d3c129e80 | ||
|
|
2bc75c8130 | ||
|
|
22d8a61544 | ||
|
|
3f9aad64b5 | ||
|
|
8c802a6e6b | ||
|
|
f2a2ac44d6 | ||
|
|
ea9c3eac30 | ||
|
|
9cf03e37c4 | ||
|
|
e0b84aaff7 | ||
|
|
8821171ff5 | ||
|
|
c03671f47d | ||
|
|
5787582c2e | ||
|
|
9d685ab785 | ||
|
|
bcbd4be7f9 | ||
|
|
54689b7dd1 | ||
|
|
740adf27f3 | ||
|
|
40578d9d5f | ||
|
|
f81babe973 | ||
|
|
f6ff6bb274 | ||
|
|
bcc249a4bd | ||
|
|
afa40dd1a1 | ||
|
|
fe8372379c | ||
|
|
2e4bea8292 | ||
|
|
b920f7d7b1 | ||
|
|
bcfbb20685 | ||
|
|
4dadfb0874 | ||
|
|
970fe43413 | ||
|
|
11aaa0cd36 | ||
|
|
6bff669abe | ||
|
|
c6edbc20af | ||
|
|
82b4fa5e7c | ||
|
|
3f1f698511 | ||
|
|
3421f286f2 | ||
|
|
bfc141f655 | ||
|
|
2f809e398c | ||
|
|
2ede1a195f | ||
|
|
6653fa2e03 | ||
|
|
5c6310b1ca | ||
|
|
369ddcc100 | ||
|
|
eaaf17a792 | ||
|
|
f60f61e616 | ||
|
|
28fcdb3122 | ||
|
|
4e2e8ddf94 | ||
|
|
d270e8f66b | ||
|
|
6ac4aa50b3 | ||
|
|
2a71fb0075 | ||
|
|
51b1219003 | ||
|
|
dd45766bae | ||
|
|
144e158473 | ||
|
|
dedb8cbf76 | ||
|
|
284d4e01a3 | ||
|
|
3d36786cc7 | ||
|
|
074c5f6766 | ||
|
|
2e21af4d6b | ||
|
|
effd028658 | ||
|
|
4a517bde5e | ||
|
|
6a526dbbe4 | ||
|
|
3568b62219 | ||
|
|
34778a4bdf | ||
|
|
ce0b2ab955 | ||
|
|
9261253b63 | ||
|
|
f6aaf85cca | ||
|
|
12910ba724 | ||
|
|
e8a69d36bb | ||
|
|
c329d21e7d | ||
|
|
4aafddfdb1 | ||
|
|
80dcfc362b | ||
|
|
4127396892 | ||
|
|
7ab81a61df | ||
|
|
65435572d4 | ||
|
|
c0abeee648 | ||
|
|
a38625d821 | ||
|
|
4e244e141f | ||
|
|
f741c2709c | ||
|
|
179947cd98 | ||
|
|
43ddab699f | ||
|
|
538ba3547f | ||
|
|
e67c549714 | ||
|
|
812de48558 | ||
|
|
52a6eb2125 | ||
|
|
c61b428d67 | ||
|
|
af4c3885ae | ||
|
|
94a8b4ad47 | ||
|
|
72cf776882 | ||
|
|
123f8992b1 | ||
|
|
77cc7fe24a | ||
|
|
5a258ce68e | ||
|
|
35e648b7bc | ||
|
|
06272778f8 | ||
|
|
2f816c33cd | ||
|
|
9b66547803 | ||
|
|
e06765abc3 | ||
|
|
2073752e7e | ||
|
|
84ec3d085d | ||
|
|
97a6e4baf2 | ||
|
|
174d67c9e1 | ||
|
|
c360ac7126 | ||
|
|
f46d4c7024 | ||
|
|
d3c50aa227 | ||
|
|
1ee8827c9c | ||
|
|
54d88f9f6c | ||
|
|
80acd0857d | ||
|
|
c12fd1a8b4 | ||
|
|
1a7dccb757 | ||
|
|
b008d48c60 | ||
|
|
2cf979401b | ||
|
|
87fcdd3c10 | ||
|
|
9f523ba3c0 | ||
|
|
e08e3b9b94 | ||
|
|
00f41a95bc | ||
|
|
b1f4efd9ac | ||
|
|
212a52fd04 | ||
|
|
0c7fadbaad | ||
|
|
a8ecd44efb | ||
|
|
ce4ba48da0 | ||
|
|
7a4cbbddff | ||
|
|
72194898ba | ||
|
|
3bd71828fa | ||
|
|
7615e56ffd | ||
|
|
d51ae7d310 | ||
|
|
3042fce50e | ||
|
|
9b6ca1faf4 | ||
|
|
b97e1efa8d | ||
|
|
86013e6576 | ||
|
|
51a68e24cf | ||
|
|
c95fb05fbf | ||
|
|
006f0f986e | ||
|
|
7769d68859 | ||
|
|
586566c595 | ||
|
|
1e76b1501f | ||
|
|
7d9d3e4d72 | ||
|
|
37610ad8b0 | ||
|
|
6271a7fc18 | ||
|
|
ad698b1a03 | ||
|
|
1b79abdab7 | ||
|
|
7a3d9022e9 | ||
|
|
3d53e85fc9 | ||
|
|
85b347d5f3 | ||
|
|
6b065dc035 | ||
|
|
df70082a81 | ||
|
|
4dd6db54fe | ||
|
|
afd999a7b0 | ||
|
|
54635072f6 | ||
|
|
bd3e9fdca6 | ||
|
|
784e611272 | ||
|
|
77b3dac1b1 | ||
|
|
3b97b22f10 | ||
|
|
20acfad2c2 | ||
|
|
539a39f83a | ||
|
|
452335d84a | ||
|
|
3cafbe8c4f | ||
|
|
a2f54b627b | ||
|
|
71b72bbdd4 | ||
|
|
1689d80721 | ||
|
|
ecbcf9a968 | ||
|
|
f9133584d4 | ||
|
|
e4833bdba9 | ||
|
|
63061e3a52 | ||
|
|
4596159b8f | ||
|
|
9eabe5323d | ||
|
|
38233ccda4 | ||
|
|
7ac0e27739 | ||
|
|
d2c720c863 | ||
|
|
0977110e79 | ||
|
|
b387b18923 | ||
|
|
d58617b173 | ||
|
|
c29b6c6fd4 | ||
|
|
4808662893 | ||
|
|
22f33324e0 | ||
|
|
cff7827ace | ||
|
|
400dd2944d | ||
|
|
144c082d3e | ||
|
|
2c10be0102 | ||
|
|
37bc018a13 | ||
|
|
630c0b0356 | ||
|
|
afcc6fe63a | ||
|
|
a6a3962044 | ||
|
|
bc4e0f835f | ||
|
|
c95b0b55be | ||
|
|
7c815022a9 | ||
|
|
2ef2998279 | ||
|
|
196526cc6a | ||
|
|
5d78a0c574 | ||
|
|
a3cc8160d3 | ||
|
|
4caeb7ec91 | ||
|
|
822b2876e9 |
6
.editorconfig
Normal file
@ -0,0 +1,6 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
2
.github/pull_request_template.md
vendored
@ -1,5 +1,5 @@
|
||||
---
|
||||
- [ ] I have reviewed CONTRIBUTING.md.
|
||||
- [ ] My commits and title use the [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/) format.
|
||||
- [ ] I have documented any breaking changes according to [SemVer](https://semver.org/).
|
||||
- [ ] I have documented changes and additions in the CHANGELOG.md.
|
||||
---
|
||||
|
||||
65
.github/workflows/build.yml
vendored
@ -1,31 +1,64 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.build }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- build: linux-amd64
|
||||
os: ubuntu-latest
|
||||
goos: [linux, windows, darwin]
|
||||
goarch: ["386", amd64, arm64]
|
||||
exclude:
|
||||
- goarch: "386"
|
||||
goos: darwin
|
||||
- goarch: arm64
|
||||
goos: windows
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.22.2'
|
||||
- name: Download Task
|
||||
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||
- name: Build
|
||||
run: make hilbiline
|
||||
- uses: actions/upload-artifact@v2
|
||||
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} ./bin/task
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.goos == 'windows'
|
||||
with:
|
||||
name: hilbish-${{ matrix.build }}
|
||||
path: hilbish
|
||||
|
||||
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: |
|
||||
hilbish.exe
|
||||
LICENSE
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
.hilbishrc.lua
|
||||
nature
|
||||
libs
|
||||
docs
|
||||
emmyLuaDocs
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.goos != 'windows'
|
||||
with:
|
||||
name: hilbish-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: |
|
||||
hilbish
|
||||
LICENSE
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
.hilbishrc.lua
|
||||
nature
|
||||
libs
|
||||
docs
|
||||
emmyLuaDocs
|
||||
|
||||
60
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
name: CodeQL
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '38 2 * * 4'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
29
.github/workflows/docs.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Generate docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
gen:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v5
|
||||
- name: Download Task
|
||||
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||
- name: Build
|
||||
run: ./bin/task
|
||||
- name: Run docgen (go-written)
|
||||
run: go run cmd/docgen/docgen.go
|
||||
- name: Run docgen (lua-written)
|
||||
run: ./hilbish cmd/docgen/docgen.lua
|
||||
- name: Commit new docs
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: "docs: [ci] generate new docs"
|
||||
file_pattern: docs/ emmyLuaDocs/
|
||||
46
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v[0-9]+.*
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: taiki-e/create-gh-release-action@v1
|
||||
with:
|
||||
title: Hilbish $tag
|
||||
changelog: CHANGELOG.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
releases-matrix:
|
||||
name: Build Release Binary
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [linux, windows, darwin]
|
||||
goarch: ["386", amd64, arm64]
|
||||
exclude:
|
||||
- goarch: "386"
|
||||
goos: darwin
|
||||
- goarch: arm64
|
||||
goos: windows
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
- name: Download Task
|
||||
run: 'sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d'
|
||||
- uses: wangyoucao577/go-release-action@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goos: ${{ matrix.goos }}
|
||||
goarch: ${{ matrix.goarch }}
|
||||
ldflags: '-s -w'
|
||||
binary_name: hilbish
|
||||
extra_files: LICENSE README.md CHANGELOG.md .hilbishrc.lua nature libs docs emmyLuaDocs
|
||||
54
.github/workflows/website.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
name: Build website
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- v[0-9]+.*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
with:
|
||||
hugo-version: '0.111.3'
|
||||
extended: true
|
||||
|
||||
- name: Set branch name
|
||||
id: branch
|
||||
run: echo "BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/*/}}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Fix base URL
|
||||
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
||||
run: sed -i "s%baseURL = 'https://rosettea.github.io/Hilbish/'%baseURL = 'https://rosettea.github.io/Hilbish/versions/${{ env.BRANCH_NAME }}'%" website/config.toml
|
||||
|
||||
- name: Build
|
||||
run: 'cd website && hugo --minify'
|
||||
|
||||
- name: Deploy
|
||||
if: env.BRANCH_NAME == 'master' && github.repository_owner == 'Rosettea'
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./website/public
|
||||
keep_files: true
|
||||
- name: Deploy
|
||||
if: env.BRANCH_NAME != 'master' && github.repository_owner == 'Rosettea'
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./website/public
|
||||
destination_dir: versions/${{ env.BRANCH_NAME }}
|
||||
keep_files: true
|
||||
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
||||
*.exe
|
||||
hilbish
|
||||
!docs/api/hilbish
|
||||
docgen
|
||||
!cmd/docgen
|
||||
|
||||
.vim
|
||||
petals/
|
||||
.hugo_build.lock
|
||||
|
||||
8
.gitmodules
vendored
@ -1,3 +1,9 @@
|
||||
[submodule "libs/lunacolors"]
|
||||
path = libs/lunacolors
|
||||
url = https://github.com/Hilbis/Lunacolors
|
||||
url = https://github.com/Rosettea/Lunacolors
|
||||
[submodule "libs/succulent"]
|
||||
path = libs/succulent
|
||||
url = https://github.com/Rosettea/Succulent
|
||||
[submodule "libs/inspect"]
|
||||
path = libs/inspect
|
||||
url = https://github.com/kikito/inspect.lua
|
||||
|
||||
@ -1,19 +1,49 @@
|
||||
-- Default Hilbish config
|
||||
lunacolors = require 'lunacolors'
|
||||
bait = require 'bait'
|
||||
local hilbish = require 'hilbish'
|
||||
local lunacolors = require 'lunacolors'
|
||||
local bait = require 'bait'
|
||||
local ansikit = require 'ansikit'
|
||||
|
||||
function doPrompt(fail)
|
||||
prompt(lunacolors.format(
|
||||
local unreadCount = 0
|
||||
local running = false
|
||||
local function doPrompt(fail)
|
||||
hilbish.prompt(lunacolors.format(
|
||||
'{blue}%u {cyan}%d ' .. (fail and '{red}' or '{green}') .. '∆ '
|
||||
))
|
||||
end
|
||||
|
||||
print(lunacolors.format('Welcome to {magenta}Hilbish{reset}, {cyan}' .. hilbish.user
|
||||
.. '{reset}.\n' .. 'The nice lil shell for {blue}Lua{reset} fanatics!\n'))
|
||||
local function doNotifyPrompt()
|
||||
if running or unreadCount == hilbish.messages.unreadCount() then return end
|
||||
|
||||
local notifPrompt = string.format('• %s unread notification%s', hilbish.messages.unreadCount(), hilbish.messages.unreadCount() > 1 and 's' or '')
|
||||
unreadCount = hilbish.messages.unreadCount()
|
||||
hilbish.prompt(lunacolors.blue(notifPrompt), 'right')
|
||||
|
||||
hilbish.timeout(function()
|
||||
hilbish.prompt('', 'right')
|
||||
end, 3000)
|
||||
end
|
||||
|
||||
doPrompt()
|
||||
|
||||
bait.catch('command.exit', function(code)
|
||||
doPrompt(code ~= 0)
|
||||
bait.catch('command.preexec', function()
|
||||
running = true
|
||||
end)
|
||||
|
||||
bait.catch('command.exit', function(code)
|
||||
running = false
|
||||
doPrompt(code ~= 0)
|
||||
doNotifyPrompt()
|
||||
end)
|
||||
|
||||
bait.catch('hilbish.vimMode', function(mode)
|
||||
if mode ~= 'insert' then
|
||||
ansikit.cursorStyle(ansikit.blockCursor)
|
||||
else
|
||||
ansikit.cursorStyle(ansikit.lineCursor)
|
||||
end
|
||||
end)
|
||||
|
||||
bait.catch('hilbish.notification', function(notif)
|
||||
doNotifyPrompt()
|
||||
end)
|
||||
|
||||
853
CHANGELOG.md
Normal file
@ -0,0 +1,853 @@
|
||||
# 🎀 Changelog
|
||||
|
||||
## Unreleased
|
||||
### Added
|
||||
- Forward/Right arrow key will fill in hint text (#327)
|
||||
- The readline library adds the ability to create custom instances of the Hilbish
|
||||
line editor. Now, `hilbish.editor` has been changed to a readline instance, instead of just being a table of a few functions to access it.
|
||||
This means the colon operator is now the *preferred* way of accessing its functions,
|
||||
and the dot operator will cause errors in 3.0.
|
||||
Example: `hilbish.editor.getLine()` should be changed to `hilbish.editor:getLine()`
|
||||
before 3.0
|
||||
- Added the `hilbish.editor:read` and `hilbish.editor:log(text)` functions.
|
||||
- `yarn` threading library (See the docs)
|
||||
|
||||
### Changed
|
||||
- Documentation for Lunacolors has been improved, with more information added.
|
||||
- Values returned by bait hooks will be passed to the `throw` caller
|
||||
- `display` property to completion groups entries to style completion entries when type is `list`.
|
||||
example:
|
||||
```lua
|
||||
local cg = {
|
||||
items = {
|
||||
'list item 1',
|
||||
['--command-flag-here'] = {'this does a thing', '--the-flag-alias'},
|
||||
['--styled-command-flag-here'] = {'this does a thing', '--the-flag-alias', display = lunacolors.blue '--styled-command-flag-here'}
|
||||
},
|
||||
type = 'list'
|
||||
}
|
||||
```
|
||||
|
||||
## [2.3.4] - 2024-12-28
|
||||
### Fixed
|
||||
- Skip over file and prevent panic if info cannot be retrieved during file completion (due to permission error or anything else)
|
||||
- Apply environment variables properly after 2.3 shell interpreter changes
|
||||
- hilbish.sink.readAll() function now reads data that doesn't end in a newline
|
||||
|
||||
## [2.3.3] - 2024-11-04
|
||||
### Fixed
|
||||
- Heredocs having issues
|
||||
|
||||
### Added
|
||||
- Adding `\` at the end of input will add a newline and prompt for more input.
|
||||
|
||||
## [2.3.2] - 2024-07-30
|
||||
### Fixed
|
||||
- Command path searching due to 2.3 changes to the shell interpreter
|
||||
|
||||
## [2.3.1] - 2024-07-27
|
||||
[hehe when you see it release](https://youtu.be/AaAF51Gwbxo?si=rhj2iYuQRkqDa693&t=64)
|
||||
|
||||
### Added
|
||||
- `hilbish.opts.tips` was added to display random tips on start up.
|
||||
Displayed tips can be modified via the `hilbish.tips` table.
|
||||
|
||||
### Fixed
|
||||
- Fix a minor regression related to the cd command not working with relative paths
|
||||
- Updated the motd for 2.3
|
||||
|
||||
## [2.3.0] - 2024-07-20
|
||||
### Added
|
||||
- `commander.registry` function to get all registered commanders.
|
||||
- `fs.pipe` function to get a pair of connected files (a pipe).
|
||||
- Added an alternative 2nd parameter to `hilbish.run`, which is `streams`.
|
||||
`streams` is a table of input and output streams to run the command with.
|
||||
It uses these 3 keys:
|
||||
- `input` as standard input for the command
|
||||
- `out` as standard output
|
||||
- `err` as standard error
|
||||
|
||||
Here is a minimal example of the new usage which allows users to now pipe commands
|
||||
directly via Lua functions:
|
||||
|
||||
```lua
|
||||
local fs = require 'fs'
|
||||
local pr, pw = fs.pipe()
|
||||
hilbish.run('ls -l', {
|
||||
stdout = pw,
|
||||
stderr = pw,
|
||||
})
|
||||
|
||||
pw:close()
|
||||
|
||||
hilbish.run('wc -l', {
|
||||
stdin = pr
|
||||
})
|
||||
```
|
||||
|
||||
### Changed
|
||||
- The `-S` flag will be set to Hilbish's absolute path
|
||||
- Hilbish now builds on any Unix (if any dependencies also work, which should.)
|
||||
|
||||
### Fixed
|
||||
- Fix ansi attributes causing issues with text when cut off in greenhouse
|
||||
- Fix greenhouse appearing on terminal resize
|
||||
- Fix crashes when history goes out of bounds when using history navigation
|
||||
- `exec` command should return if no arg presented
|
||||
- Commanders can now be cancelled by Ctrl-C and wont hang the shell anymore.
|
||||
See [issue 198](https://github.com/Rosettea/Hilbish/issues/198).
|
||||
- Shell interpreter can now preserve its environment and set PWD properly.
|
||||
|
||||
## [2.2.3] - 2024-04-27
|
||||
### Fixed
|
||||
- Highligher and hinter work now, since it was regressed from the previous minor release.
|
||||
- `cat` command no longer prints extra newline at end of each file
|
||||
|
||||
### Added
|
||||
- `cat` command now reads files in chunks, allowing for reading large files
|
||||
|
||||
## [2.2.2] - 2024-04-16
|
||||
### Fixed
|
||||
- Line refresh fixes (less flicker)
|
||||
- Do more checks for a TTY
|
||||
- Panic if ENOTTY is thrown from readline
|
||||
- use `x/term` function to check if a terminal
|
||||
|
||||
### Added
|
||||
- Page Up/Down keybinds for Greenhouse will now scroll up and down the size of the region (a page)
|
||||
|
||||
### Changed
|
||||
- Remove usage of `hilbish.goro` in Greenhouse.
|
||||
- Values in `hilbish` table are no longer protected. This means
|
||||
they can be overridden. (#287)
|
||||
|
||||
## [2.2.1] - 2023-12-26
|
||||
### Fixed
|
||||
- Removed a left over debug print
|
||||
- Recover panic in `hilbish.goro`
|
||||
|
||||
## [2.2.0] - 2023-12-25
|
||||
### Added
|
||||
- [Native Modules](https://rosettea.github.io/Hilbish/docs/api/hilbish/hilbish.module/)
|
||||
- Made a few additions to the sink type:
|
||||
- `read()` method for retrieving input (so now the `in` sink of commanders is useful)
|
||||
- `flush()` and `autoFlush()` related to flushing outputs
|
||||
- `pipe` property to check if a sink with input is a pipe (like stdin)
|
||||
- Add fuzzy search to history search (enable via `hilbish.opts.fuzzy = true`)
|
||||
- Show indexes on cdr list and use ~ for home directory.
|
||||
- Fix doc command not displaying correct subdocs when using shorthand api doc access (`doc api hilbish.jobs` as an example)
|
||||
- `hilbish.messages` interface (details in [#219])
|
||||
- `hilbish.notification` signal when a message/notification is sent
|
||||
- `notifyJobFinish` opt to send a notification when background jobs are
|
||||
- `hilbish.goVersion` for the version of Go used to compile Hilbish.
|
||||
completed.
|
||||
- Allow numbered arg substitutions in aliases.
|
||||
- Example: `hilbish.alias('hello', 'echo %1 says hello')` allows the user to run `hello hilbish`
|
||||
which will output `hilbish says hello`.
|
||||
- Greenhouse
|
||||
- Greenhouse is a pager library and program. Basic usage is `greenhouse <file>`
|
||||
- Using this also brings enhancements to the `doc` command like easy
|
||||
navigation of neighboring doc files.
|
||||
Ctrl-N can be used for the table of contents, which views adjacent documentation.
|
||||
|
||||
### Changed
|
||||
- Documentation for EVERYTHING has been improved, with more
|
||||
information added, code example, parameter details, etc.
|
||||
You can see the improvements!
|
||||
- Documentation has gotten an uplift in the `doc` command.
|
||||
This includes:
|
||||
- Proper highlighting of code
|
||||
- Paging (via Greenhouse)
|
||||
- Highlighting more markdown things
|
||||
|
||||
### Fixed
|
||||
- Fix panic when runner doesn't return a table
|
||||
- Fix edge case of crash on empty alias resolve
|
||||
- File completion on Windows
|
||||
- Job management commands work now
|
||||
- Fix infinite loop when navigating history without any history. [#252](https://github.com/Rosettea/Hilbish/issues/252)
|
||||
- Return the prefix when calling `hilbish.completions.call`. [#219](https://github.com/Rosettea/Hilbish/issues/219)
|
||||
- Replaced `sed` in-place editing with `grep` and `mv` for compatibility with BSD utils
|
||||
|
||||
## [2.1.2] - 2022-04-10
|
||||
### Removed
|
||||
- Bad april fools code ;(
|
||||
|
||||
## [2.1.1] - 2022-04-01
|
||||
### Added
|
||||
- Validation checks for command input
|
||||
- Improved runtime performance
|
||||
- Validate Lua code
|
||||
|
||||
## [2.1.0] - 2022-02-10
|
||||
### Added
|
||||
- Documented custom userdata types (Job and Timer Objects)
|
||||
- Coming with this fix is also adding the return types for some functions that were missing it
|
||||
- Added a dedicated input and dedicated outputs for commanders (sinks - info at `doc api commander`).
|
||||
- Local docs is used if one of Hilbish's branches is found
|
||||
- Return 1 exit code on doc not found
|
||||
- `hilbish.runner.getCurrent()` to get the current runner
|
||||
- Initialize Hilbish Lua API before handling signals
|
||||
|
||||
### Fixed
|
||||
- `index` or `_index` subdocs should not show up anymore
|
||||
- `hilbish.which` not working correctly with aliases
|
||||
- Commanders not being able to pipe with commands or any related operator.
|
||||
- Resolve symlinks in completions
|
||||
- Updated `runner-mode` docs
|
||||
- Fix `hilbish.completion` functions panicking when empty input is provided
|
||||
|
||||
## [2.0.1] - 2022-12-28
|
||||
### Fixed
|
||||
- Corrected documentation for hooks, removing outdated `command.no-perm`
|
||||
- Fixed an issue where `cd` with no args would not update the old pwd
|
||||
- Tiny documentation enhancements for the `hilbish.timer` interface
|
||||
|
||||
## [2.0.0] - 2022-12-20
|
||||
**NOTES FOR USERS/PACKAGERS UPDATING:**
|
||||
- Hilbish now uses [Task] insead of Make for builds.
|
||||
- The doc format has been changed from plain text to markdown.
|
||||
**YOU MUST reinstall Hilbish to remove the duplicate, old docs.**
|
||||
- Hilbish will by default install to **`/usr/local`** instead of just `/usr/`
|
||||
when building via Task. This is mainly to avoid conflict of distro packages
|
||||
and local installs, and is the correct place when building from git either way.
|
||||
To keep Hilbish in `/usr`, you must have `PREFIX="/usr/"` when running `task build` or `task install`
|
||||
- Windows is no longer supported. It will build and run, but **will** have problems.
|
||||
If you want to help fix the situation, start a discussion or open an issue and contribute.
|
||||
|
||||
[Task]: https://taskfile.dev/#/
|
||||
|
||||
### Added
|
||||
- Inline hints, akin to fish and the others.
|
||||
To make a handler for hint text, you can set the `hilbish.hinter` function.
|
||||
For more info, look at its docs with the `doc hilbish` command.
|
||||
- Syntax highlighting function. To make a handler for it, set
|
||||
`hilbish.highlighter`. Same thing as the hinter, check `doc hilbish` for
|
||||
more info/docs.
|
||||
- Ctrl+K deletes from the cursor to the end of the line. ([#128](https://github.com/Rosettea/Hilbish/pull/128))
|
||||
- Alt+Backspace as an alternative of Ctrl+W to delete a word. ([#132](https://github.com/Rosettea/Hilbish/pull/132))
|
||||
- Enhanced timer API (`doc timers`)
|
||||
- Don't exit until intervals are stopped/finished when running a non interactive script.
|
||||
- Ctrl+D deletes character below cursor if line isn't empty instead of exiting.
|
||||
- Ctrl+Delete to forward delete a word.
|
||||
- Right prompt ([#140](https://github.com/Rosettea/Hilbish/pull/140))
|
||||
- Ctrl+_ to undo in Emacs input mode.
|
||||
- Emacs style forward/backward word keybinds ([#139](https://github.com/Rosettea/Hilbish/pull/139))
|
||||
- `hilbish.completion.call` to call a completion handler (`doc completions`)
|
||||
- `hilbish.completion.handler` to set a custom handler for completions. This
|
||||
is for everything/anything as opposed to just adding a single command completion.
|
||||
[#122](https://github.com/Rosettea/Hilbish/issues/122)
|
||||
- `fs.abs(path)` to get absolute path.
|
||||
- Nature module (`doc nature`)
|
||||
- `hilbish.jobs.add(cmdstr, args, execPath)` to add a job to the job table.
|
||||
`cmdstr` would be user input, `args` is the args for the command (includes arg0)
|
||||
and `execPath` is absolute path to command executable
|
||||
- `job.add` hook is thrown when a job is added. acts as a unique hook for
|
||||
jobs
|
||||
- `hilbish.jobs.disown(id)` and `disown` builtin to disown a job. `disown`
|
||||
without arguments will disown the last job.
|
||||
- `hilbish.jobs.last()` returns the last added job.
|
||||
- Job output (stdout/stderr) can now be obtained via the `stdout` and `stderr`
|
||||
fields on a job object.
|
||||
- Documentation for jobs is now available via `doc jobs`.
|
||||
- `hilbish.alias.resolve(cmdstr)` to resolve a command alias.
|
||||
- `hilbish.opts` for shell options.
|
||||
- `hilbish.editor` interface for interacting with the line editor that
|
||||
Hilbish uses.
|
||||
- `hilbish.vim` interface to dynamically get/set vim registers.
|
||||
Example usage: `hilbish.vim.registers['a'] = 'hello'`. You can also
|
||||
get the mode with it via `hilbish.vim.mode`
|
||||
- `hilbish.version` interface for more info about Hilbish's version. This
|
||||
includes git commit, branch, and (new!!) release name.
|
||||
- Added `fg` and `bg` builtins
|
||||
- `job.foreground()` and `job.background()`, when `job` is a job object,
|
||||
foreground and backgrounds a job respectively.
|
||||
- Friendlier functions to the `hilbish.runner` interface, which also allow
|
||||
having and using multiple runners.
|
||||
- A few new functions to the `fs` module:
|
||||
- `fs.basename(path)` gets the basename of path
|
||||
- `fs.dir(path)` gets the directory part of path
|
||||
- `fs.glob(pattern)` globs files and directories based on patterns
|
||||
- `fs.join(dirs...)` joins directories by OS dir separator
|
||||
- .. and 2 properties
|
||||
- `fs.pathSep` is the separator for filesystem paths and directories
|
||||
- `fs.pathListSep` is the separator for $PATH env entries
|
||||
- Lua modules located in `hilbish.userDir.data .. '/hilbish/start'` (like `~/.local/share/hilbish/start/foo/init.lua`)
|
||||
will be ran on startup
|
||||
- `hilbish.init` hook, thrown after Hilbish has initialized Lua side
|
||||
- Message of the day on startup (`hilbish.motd`), mainly intended as quick
|
||||
small news pieces for releases. It is printed by default. To disable it,
|
||||
set `hilbish.opts.motd` to false.
|
||||
- `history` opt has been added and is true by default. Setting it to false
|
||||
disables commands being added to history.
|
||||
- `hilbish.rawInput` hook for input from the readline library
|
||||
- Completion of files in quotes
|
||||
- A new and "safer" event emitter has been added. This causes a performance deficit, but avoids a lot of
|
||||
random errors introduced with the new Lua runtime (see [#197])
|
||||
- `bait.release(name, catcher)` removes `handler` for the named `event`
|
||||
- `exec`, `clear` and `cat` builtin commands
|
||||
- `hilbish.cancel` hook thrown when user cancels input with Ctrl-C
|
||||
- 1st item on history is now inserted when history search menu is opened ([#148])
|
||||
- Documentation has been improved vastly!
|
||||
|
||||
[#148]: https://github.com/Rosettea/Hilbish/issues/148
|
||||
[#197]: https://github.com/Rosettea/Hilbish/issues/197
|
||||
|
||||
### Changed
|
||||
- **Breaking Change:** Upgraded to Lua 5.4.
|
||||
This is probably one of (if not the) biggest things in this release.
|
||||
To recap quickly on what matters (mostly):
|
||||
- `os.execute` returns 3 values instead of 1 (but you should be using `hilbish.run`)
|
||||
- I/O operations must be flushed (`io.flush()`)
|
||||
- **Breaking Change:** MacOS config paths now match Linux.
|
||||
- Overrides on the `hilbish` table are no longer permitted.
|
||||
- **Breaking Change:** Runner functions are now required to return a table.
|
||||
It can (at the moment) have 4 variables:
|
||||
- `input` (user input)
|
||||
- `exitCode` (exit code)
|
||||
- `error` (error message)
|
||||
- `continue` (whether to prompt for more input)
|
||||
User input has been added to the return to account for runners wanting to
|
||||
prompt for continued input, and to add it properly to history. `continue`
|
||||
got added so that it would be easier for runners to get continued input
|
||||
without having to actually handle it at all.
|
||||
- **Breaking Change:** Job objects and timers are now Lua userdata instead
|
||||
of a table, so their functions require you to call them with a colon instead
|
||||
of a dot. (ie. `job.stop()` -> `job:stop()`)
|
||||
- All `fs` module functions which take paths now implicitly expand ~ to home.
|
||||
- **Breaking Change:** `hilbish.greeting` has been moved to an opt (`hilbish.opts.greeting`) and is
|
||||
always printed by default. To disable it, set the opt to false.
|
||||
- **Breaking Change:** `command.no-perm` hook has been replaced with `command.not-executable`
|
||||
- History is now fetched from Lua, which means users can override `hilbish.history`
|
||||
methods to make it act how they want.
|
||||
- `guide` has been removed. See the [website](https://rosettea.github.io/Hilbish/)
|
||||
for general tips and documentation
|
||||
|
||||
### Fixed
|
||||
- If in Vim replace mode, input at the end of the line inserts instead of
|
||||
replacing the last character.
|
||||
- Make forward delete work how its supposed to.
|
||||
- Prompt refresh not working properly.
|
||||
- Crashing on input in xterm. ([#131](https://github.com/Rosettea/Hilbish/pull/131))
|
||||
- Make delete key work on st ([#131](https://github.com/Rosettea/Hilbish/pull/131))
|
||||
- `hilbish.login` being the wrong value.
|
||||
- Put full input in history if prompted for continued input
|
||||
- Don't put alias expanded command in history (sound familiar?)
|
||||
- Handle cases of stdin being nonblocking (in the case of [#136](https://github.com/Rosettea/Hilbish/issues/136))
|
||||
- Don't prompt for continued input if non interactive
|
||||
- Don't insert unhandled control keys.
|
||||
- Handle sh syntax error in alias
|
||||
- Use invert for completion menu selection highlight instead of specific
|
||||
colors. Brings an improvement on light themes, or themes that don't follow
|
||||
certain color rules.
|
||||
- Home/End keys now go to the actual start/end of the input.
|
||||
- Input getting cut off on enter in certain cases.
|
||||
- Go to the next line properly if input reaches end of terminal width.
|
||||
- Cursor position with CJK characters has been corrected ([#145](https://github.com/Rosettea/Hilbish/pull/145))
|
||||
- Files with same name as parent folder in completions getting cut off [#130](https://github.com/Rosettea/Hilbish/issues/130))
|
||||
- `hilbish.which` now works with commanders and aliases.
|
||||
- Background jobs no longer take stdin so they do not interfere with shell
|
||||
input.
|
||||
- Full name of completion entry is used instead of being cut off
|
||||
- Completions are fixed in cases where the query/line is an alias alone
|
||||
where it can also resolve to the beginning of command names.
|
||||
(reference [this commit](https://github.com/Rosettea/Hilbish/commit/2790982ad123115c6ddbc5764677fdca27668cea))
|
||||
for explanation.
|
||||
- Jobs now throw `job.done` and set running to false when stopped via
|
||||
Lua `job.stop` function.
|
||||
- Jobs are always started in sh exec handler now instead of only successful start.
|
||||
- SIGTERM is handled properly now, which means stopping jobs and timers.
|
||||
- Fix panic on trailing newline on pasted multiline text.
|
||||
- Completions will no longer be refreshed if the prompt refreshes while the
|
||||
menu is open.
|
||||
- Print error on search fail instead of panicking
|
||||
- Windows related fixes:
|
||||
- `hilbish.dataDir` now has tilde (`~`) expanded.
|
||||
- Arrow keys now work on Windows terminals.
|
||||
- Escape codes now work.
|
||||
- Escape percentage symbols in completion entries, so you will no longer see
|
||||
an error of missing format variable
|
||||
- Fix an error with sh syntax in aliases
|
||||
- Prompt now works with east asian characters (CJK)
|
||||
- Set back the prompt to normal after exiting the continue prompt with ctrl-d
|
||||
- Take into account newline in input when calculating input width. Prevents
|
||||
extra reprinting of the prompt, but input with newlines inserted is still a problem
|
||||
- Put cursor at the end of input when exiting $EDITOR with Vim mode bind
|
||||
- Calculate width of virtual input properly (completion candidates)
|
||||
- Users can now tab complete files with spaces while quoted or with escaped spaces.
|
||||
This means a query of `Files\ to\ ` with file names of `Files to tab complete` and `Files to complete`
|
||||
will result in the files being completed.
|
||||
- Fixed grid menu display if cell width ends up being the width of the terminal
|
||||
- Cut off item names in grid menu if its longer than cell width
|
||||
- Fix completion search menu disappearing
|
||||
- Make binary completion work with bins that have spaces in the name
|
||||
- Completion paths having duplicated characters if it's escaped
|
||||
- Get custom completion command properly to call from Lua
|
||||
- Put proper command on the line when using up and down arrow keys to go through command history
|
||||
- Don't do anything if length of input rune slice is 0 ([commit for explanation](https://github.com/Rosettea/Hilbish/commit/8d40179a73fe5942707cd43f9c0463dee53eedd8))
|
||||
|
||||
## [2.0.0-rc1] - 2022-09-14
|
||||
This is a pre-release version of Hilbish for testing. To see the changelog,
|
||||
refer to the `Unreleased` section of the [full changelog](CHANGELOG.md)
|
||||
(version 2.0.0 for future reference).
|
||||
|
||||
## [1.2.0] - 2022-03-17
|
||||
### Added
|
||||
- Job Management additions
|
||||
- `job.start` and `job.done` hooks (`doc hooks job`)
|
||||
- `hilbish.jobs` interface (`get(id)` function gets a job object via `id`, `all()` gets all)
|
||||
- Customizable runner/exec mode
|
||||
- However Hilbish runs interactive user input can now be changed Lua side (`doc runner-mode`)
|
||||
|
||||
### Changed
|
||||
- `vimMode` doc is now `vim-mode`
|
||||
|
||||
### Fixed
|
||||
- Make sure input which is supposed to go in history goes there
|
||||
- Cursor is right at the end of input on history search
|
||||
|
||||
## [1.1.0] - 2022-03-17
|
||||
### Added
|
||||
- `hilbish.vimAction` hook (`doc vimMode actions`)
|
||||
- `command.not-executable` hook (will replace `command.no-perm` in a future release)
|
||||
|
||||
### Fixed
|
||||
- Check if interactive before adding to history
|
||||
- Escape in vim mode exits all modes and not only insert
|
||||
- Make 2nd line in prompt empty if entire prompt is 1 line
|
||||
- Completion menu doesnt appear if there is only 1 result
|
||||
- Ignore SIGQUIT, which caused a panic unhandled
|
||||
- Remove hostname in greeting on Windows
|
||||
- Handle PATH binaries properly on Windows
|
||||
- Fix removal of dot in the beginning of folders/files that have them for file complete
|
||||
- Fix prompt being set to the continue prompt even when exited
|
||||
|
||||
## [1.0.4] - 2022-03-12
|
||||
### Fixed
|
||||
- Panic when history directory doesn't exist
|
||||
|
||||
## [1.0.3] - 2022-03-12
|
||||
### Fixed
|
||||
- Removed duplicate executable suggestions
|
||||
- User input is added to history now instead of what's ran by Hilbish
|
||||
- Formatting issue with prompt on no input
|
||||
|
||||
## [1.0.2] - 2022-03-06
|
||||
### Fixed
|
||||
- Cases where Hilbish's history directory doesn't exist will no longer cause a panic
|
||||
|
||||
## [1.0.1] - 2022-03-06
|
||||
### Fixed
|
||||
- Using `hilbish.appendPath` will no longer result in string spam (debugging thing left being)
|
||||
- Prompt gets set properly on startup
|
||||
|
||||
## [1.0.0] - 2022-03-06
|
||||
### Added
|
||||
- MacOS is now officialy supported, default compile time vars have been added
|
||||
for it
|
||||
- Windows is properly supported as well
|
||||
- `catchOnce()` to bait - catches a hook once
|
||||
- `hilbish.aliases` interface - allows you to add, delete and list all aliases
|
||||
with Lua
|
||||
- `hilbish.appendPath()` can now take a table of arguments for ease of use
|
||||
- `hilbish.which(binName)` acts as the which builtin for other shells,
|
||||
it finds the path to `binName` in $PATH
|
||||
- Signal hooks `sigusr1` and `sigusr2` (unavailable on Windows)
|
||||
- Commands starting with a space won't be added to history
|
||||
- Vim input mode
|
||||
- Hilbish's input mode for text can now be changed to either Emacs
|
||||
(like it always was) or Vim via `hilbish.inputMode()`
|
||||
- Changing Vim mode throws a `hilbish.vimMode` hook
|
||||
- The current Vim mode is also accessible with the `hilbish.vimMode` property
|
||||
- Print errors in `hilbish.timeout()` and `hilbish.goro()` callbacks
|
||||
- `hilbish.exit` hook is thrown when Hilbish is going to exit
|
||||
- `hilbish.exitCode` property to get the exit code of the last executed command
|
||||
- `screenMain` and `screenAlt` functions have been added to Ansikit to switch
|
||||
to the terminal's main and alt buffer respectively
|
||||
|
||||
### Fixed
|
||||
- Tab completion for executables
|
||||
- Stop interval (`hilbish.interval()`) when an error occurs
|
||||
- Errors in bait hooks no longer cause a panic, and remove the handler for the hook as well
|
||||
- Formatting of home dir to ~
|
||||
- Check if Hilbish is in interactive before trying to use its handlers for signals
|
||||
- Global `args` table when running as script is no longer userdata
|
||||
- Home dir is now added to recent dirs (the case of cd with no arg)
|
||||
- `index` subdoc will no longer appear
|
||||
- Alias expansion with quotes
|
||||
- Add full command to history in the case of incomplete input
|
||||
- `hilbish.exec()` now has a windows substitute
|
||||
- Fixed case of successful command after prompted for more input not writing to history
|
||||
- `command.exit` is thrown when sh input is incorrect and when command executed after continue
|
||||
prompt exits successfully
|
||||
|
||||
### Changed
|
||||
- The minimal config is truly minimal now
|
||||
- Default config is no longer copied to user's config and is instead ran its location
|
||||
#### Breaking Changes
|
||||
(there were a lot...)
|
||||
- Change default SHLVL to 0 instead of 1
|
||||
- ~/.hilbishrc.lua will no longer be run by default, it now
|
||||
only uses the paths mentioned below.
|
||||
- Changed Hilbish's config path to something more suited
|
||||
according to the OS (`$XDG_CONFIG_HOME/hilbish/init.lua` on Linux,
|
||||
`~/Library/Application Support/hilbish/init.lua` on MacOS and
|
||||
(`%APPDATA%/hilbish/init.lua` on Windows). Previously on Unix-like it was
|
||||
`$XDG_CONFIG_HOME/hilbish/hilbishrc.lua`
|
||||
- The history path has been changed to a better suited path.
|
||||
On Linux, it is `$XDG_DATA_HOME/hilbish/.hilbish-history` and for others it is
|
||||
the config path.
|
||||
- `hilbish.xdg` no longer exists, use `hilbish.userDir` instead,
|
||||
as it functions the same but is OS agnostic
|
||||
- `hilbish.flag()` has been removed
|
||||
- `~/.hprofile.lua` has been removed, instead check in your config if `hilbish.login`
|
||||
is true
|
||||
- `hilbish.complete()` has had a slight refactor to fit with the new readline library.
|
||||
It now expects a table of "completion groups" which are just tables with the
|
||||
`type` and `items` keys. Here is a (more or less) complete example of how it works now:
|
||||
```lua
|
||||
hilbish.complete('command.git', function()
|
||||
return {
|
||||
{
|
||||
items = {
|
||||
'add',
|
||||
'clone'
|
||||
},
|
||||
type = 'grid'
|
||||
},
|
||||
{
|
||||
items = {
|
||||
['--git-dir'] = {'Description of flag'},
|
||||
'-c'
|
||||
},
|
||||
type = 'list'
|
||||
}
|
||||
}
|
||||
end)
|
||||
```
|
||||
Completer functions are now also expected to handle subcommands/subcompletions
|
||||
|
||||
## [0.7.1] - 2021-11-22
|
||||
### Fixed
|
||||
- Tab complete absolute paths to binaries properly
|
||||
- Allow execution of absolute paths to binaries (https://github.com/Rosettea/Hilbish/commit/06272778f85dad04e0e7abffc78a5b9b0cebd067 regression)
|
||||
|
||||
## [0.7.0] - 2021-11-22
|
||||
### Added
|
||||
- `hilbish.interactive` and `hilbish.login` properties to figure out if Hilbish is interactive or a login shell, respectively.
|
||||
- `hilbish.read` function to take input more elegantly than Lua's `io.read`
|
||||
- Tab Completion Enhancements
|
||||
- A new tab complete API has been added. It is the single `complete` function which takes a "scope" (example: `command.<cmdname>`) and a callback which is
|
||||
expected to return a table. Users can now add custom completions for specific commands.
|
||||
An example is:
|
||||
```lua
|
||||
complete('command.git', function()
|
||||
return {
|
||||
'add',
|
||||
'version',
|
||||
commit = {
|
||||
'--message',
|
||||
'--verbose',
|
||||
'<file>'
|
||||
}
|
||||
}
|
||||
end)
|
||||
```
|
||||
For `git`, Hilbish will complete commands add, version and commit. For the commit subcommand, it will complete the flags and/or files which `<file>` is used to represent.
|
||||
- Hilbish will now complete binaries in $PATH, or any executable to a path (like `./` or `../`)
|
||||
- Files with spaces will be automatically put in quotes and completions will work for them now.
|
||||
- `prependPath` function (#81)
|
||||
- Signal hooks (#80)
|
||||
- This allows scripts to add their own way of handling terminal resizes (if you'd need that) or Ctrl-C
|
||||
- Module properties (like `hilbish.ver`) are documented with the `doc` command.
|
||||
- Document bait hooks
|
||||
|
||||
### Fixed
|
||||
- The prompt won't come up on terminal resize anymore.
|
||||
- `appendPath` should work properly on Windows.
|
||||
- A panic when a commander has an error has been fixed.
|
||||
|
||||
## [0.6.1] - 2021-10-21
|
||||
### Fixed
|
||||
- Require paths now use the `dataDir` variable so there is no need to change it anymore unless you want to add more paths
|
||||
- Remove double slash in XDG data require paths
|
||||
- Ctrl+C is handled properly when not interactive and won't result in a panic anymore
|
||||
- Commanders are handled by the sh interpreter library only now, so they work with sh syntax
|
||||
|
||||
### Changed
|
||||
- Error messages from `fs` functions now include the path provided
|
||||
|
||||
## [0.6.0] - 2021-10-17
|
||||
### Added
|
||||
- Hilbish will expand `~` in the preloadPath and samplePathConf variables. These are for compile time.
|
||||
- On Windows, the hostname in `%u` has been removed.
|
||||
- Made it easier to compile on Windows by adding Windows-tailored vars and paths.
|
||||
- Add require paths `./libs/?/?.lua`
|
||||
- Hilbish will now respect $XDG_CONFIG_HOME and will load its config and history there first and use Lua libraries in there and $XDG_DATA_HOME if they are set. (#71)
|
||||
- If not, Hilbish will still default to `~`
|
||||
- Added some new hooks
|
||||
- `command.precmd` is thrown right before Hilbish prompts for input
|
||||
- `command.preexec` is thrown right before Hilbish executes a command. It passes 2 arguments: the command as the user typed, and what Hilbish will actually execute (resolved alias)
|
||||
- `hilbish.dataDir` is now available to know the directory of Hilbish data files (default config, docs, preload, etc)
|
||||
- A `docgen` program has been added to `cmd/docgen` in the GitHub repository, As the name suggests, it will output docs in a `docs` folder for functions implemented in Go
|
||||
- All hilbish modules/libraries now have a `__doc` metatable entry which is simply a short description of the module.
|
||||
- `fs.readdir(dir)` has been added. It will return a table of files in `dir`
|
||||
- Errors in the `fs.mkdir` function are now handled.
|
||||
- **Breaking Change:** `fs.cd` no longer returns a numeric code to indicate error. Instead, it returns an error message.
|
||||
- The `doc` command has been added to document functions of Hilbish libraries. Run the command for more details.
|
||||
- `link(url, text)` has been added to `ansikit`. It returns a string which can be printed to produce a hyperlink in a terminal. Note that not all terminals support this feature.
|
||||
- The [Succulent](https://github.com/Rosettea/Succulent) library has been added. This includes more utility functions and expansions to the Lua standard library itself.
|
||||
- The command string is now passed to the `command.exit` hook
|
||||
|
||||
### Changed
|
||||
- Hilbish won't print an extra newline at exit with ctrl + d
|
||||
- `command.exit` with 0 exit code will now be thrown if input is nothing
|
||||
- **Breaking Change:** `fs.stat` has been made better. It returns a proper table instead of userdata, and has fields instead of functions
|
||||
- It includes `name`, `mode` as a octal representation in a string, `isDir`, and `size`
|
||||
|
||||
### Fixed
|
||||
- `timeout()` is now blocking
|
||||
- Directories with spaces in them can now be `cd`'d to
|
||||
- An alias with the same name as the command will now not cause a freeze (#73)
|
||||
- Userdata is no longer returned in the following cases:
|
||||
- Commander arguments
|
||||
- `fs` functions
|
||||
|
||||
## [0.5.1] - 2021-06-16
|
||||
|
||||
### Added
|
||||
|
||||
- Add `~/.config/hilbish` as a require path
|
||||
|
||||
### Changed
|
||||
|
||||
- `cd` hook is only thrown after directory has actually changed
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handle error in commander properly, preventing a panic from Lua
|
||||
|
||||
## [0.5.0] - 2021-06-12
|
||||
|
||||
An absolutely massive release. Probably the biggest yet, includes a bunch of fixes and new features and convenient additions to the Lua API.
|
||||
|
||||
### Added
|
||||
|
||||
- `-n` flag, which checks Lua for syntax errors without running it
|
||||
- `exec(command)` function, acts like the `exec` builtin in sh
|
||||
- Example: `exec 'awesome'` in an .xinitrc file with Hilbish as shebang
|
||||
- Commands from commander can now `return` an exit code
|
||||
```lua
|
||||
commander.register('false', function()
|
||||
return 1
|
||||
end)
|
||||
```
|
||||
When `false` is run, it will have the exit code of `1`, this is shorter/easier than throwing the command.exit hook and can work if the functionality of that changes
|
||||
- Added `-c` description
|
||||
- `args` variable, set when Hilbish runs a Lua script. It is an array that includes the execute path as the first argument
|
||||
- Lua code can be aliased
|
||||
- Recursive aliases
|
||||
- At the moment this only works for the first argument
|
||||
- Hilbish can now be used with Hilbiline if compiled to do so (currently only for testing purposes)
|
||||
- `goro(func)` runs a `func`tion in a goroutine. With channels that gopher-lua also provides, one can do parallelism and concurrency in Lua (but go style).
|
||||
- `coroutine` no those dont exist they dont matter `goro` is easier
|
||||
- `cd -` will change to the previous directory
|
||||
- `hilbish.cwd()` gets the current working directory
|
||||
- `timeout(func, time)` works exactly like the `setTimeout` function in JavaScript. It will run `func` after a period of `time` in milliseconds.
|
||||
- `interval(func, time)` works exactly like the `setInterval` function in JavaScripit. It will run `func` every `time` milliseconds
|
||||
- `hilbish.home` is a crossplatform Lua alternative to get the home directory easily.
|
||||
- `commander.deregister(cmdName)` de-registers any command defined with commander.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Breaking Change**: Move `_user` and `_ver` to a global `hilbish` table
|
||||
- Accessing username and Hilbish version is now done with `hilbish.user` and `hilbish.ver`
|
||||
- `hilbish.run(cmd)` runs cmd with Hilbish's sh interpreter. Using this function instead of `os.execute` ensures that sh syntax works everywhere Hilbish does.
|
||||
- `hilbish.flag(flag)` checks if flag has been passed to Hilbish.
|
||||
- Aliases now work with every command and not only the first one
|
||||
- Therefore `alias1; alias2` works now
|
||||
- `command.not-found` hook
|
||||
- `$SHLVL` is now incremented in Hilbish. If not a valid number, it will be changed to 1
|
||||
- `fs.mkdir` can now make directories recursively if the 2nd argument is set to `true`
|
||||
- `fs.mkdir('path/to/dir', true)`
|
||||
- Hilbish runs a `preload.lua` file in the current directory first, then falls back to the global preload. Before the order was reversed.
|
||||
- Check if aliased command is defined in Lua, so registered `commander`s can be aliased
|
||||
- Add input to history before alias expansion. Basically, this adds the actual alias to history instead of the aliased command.
|
||||
- Global preload path, require paths, default config directory and sample config directory can now be changed at compile time to help support other systems.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `cd` now exits with code `1` instead of the error code if it occurs
|
||||
- Don't append directory to $PATH with `appendPath` if its already there
|
||||
- Continued input is no longer joined with a space unless explicitly wanted
|
||||
- Hilbish won't try to go interactive if it isn't launched in a TTY (terminal)
|
||||
- Ctrl+D on a continue prompt with no input no longer causes a panic
|
||||
- Actually handle the `-h`/`--help` option
|
||||
|
||||
## [0.4.0] - 2021-05-01
|
||||
|
||||
### Added
|
||||
- Ctrl C in the prompt now cancels/clear input (I've needed this for so long also)
|
||||
- Made Hilbish act like a login shell on login
|
||||
- If Hilbish is the login shell, or the `-l`/`--login` flags are used, Hilbish will use an additional `~/.hprofile.lua` file, you can use this to set environment variables once on login
|
||||
- `-c` has been added to run a single command (this works exactly like being in the prompt would, so Lua works as well)
|
||||
- `-i` (also `--interactive`) has been added to force Hilbish to be an interactive shell in cases where it usually wont be (like with `-c`)
|
||||
- Use readline in continue prompt
|
||||
- Added a `mulitline` hook that's thrown when in the continue/multiline prompt
|
||||
- Added `appendPath` function to append a directory to `$PATH`
|
||||
- `~` will be expanded to `$HOME` as well
|
||||
- A utility `string.split` function is now added
|
||||
- `string.split(str, delimiter)`
|
||||
- Added a `_user` variable to easily get current user's name
|
||||
|
||||
### Changed
|
||||
|
||||
- **BREAKING Change**: [Lunacolors](https://github.com/Hilbis/Lunacolors) has replaced ansikit for formatting colors, which means the format function has been removed from ansikit and moved to Lunacolors.
|
||||
- Users must replace ansikit with `lunacolors` in their config files
|
||||
- A getopt-like library is now used for command line flag parsing
|
||||
- `cd` builtin now supports using environment variables
|
||||
- This means you can now `cd $NVM_DIR` as an example
|
||||
- Function arguments are now more strictly typed (`prompt(nil)` wouldnt work now)
|
||||
- Other general code/style changes
|
||||
|
||||
## Fixed
|
||||
|
||||
- Fix makefile adding Hilbish to `/etc/shells` on every `make install`
|
||||
|
||||
Since Lunacolors is a submodule, you may just want to completely reclone Hilbish recursively and then update (rerun `make install`)
|
||||
Or instead of recloning, run `git submodule update --init --recursive` in Hilbish's git directory
|
||||
|
||||
## [0.3.2] - 2021-04-10
|
||||
|
||||
### Added
|
||||
|
||||
- Add more functions to `ansikit` module
|
||||
- Add functions `stat` and `mkdir` to `fs` module
|
||||
- `-C` flag to define path to config
|
||||
- Add require path `~/.local/share/hilbish/libs`
|
||||
|
||||
### Changed
|
||||
|
||||
- Continue to new line if output doesnt end with it
|
||||
|
||||
Observed:
|
||||
|
||||

|
||||
|
||||
## [0.3.1] - 2021-04-06
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `%u` in prompt format being full name and instead make it actually username
|
||||
|
||||
## [0.3.0] - 2021-04-05
|
||||
|
||||
### Added
|
||||
|
||||
- Added a `multiprompt` function to change the prompt of the multiline/continuation/newline prompt
|
||||
- `_ver` variable to get Hilbish's version from Lua
|
||||
|
||||
### Changed
|
||||
|
||||
- **BREAKING Change**: Removed Bait hooks `command.success` and `command.fail`, there is now the single hook `command.exit`, with a single argument passed which the exit code of the command. Use this to determine if a command has failed or not (failure is code != 0)
|
||||
- **BREAKING Change**: The Ansikit function `text` has been renamed to `format`.
|
||||
- `fs.cd` now throws an exception instead of silently failing, which you should handle with `pcall`
|
||||
- Enhancements to the `cd` command:
|
||||
- With no arguments will move to $HOME
|
||||
- Now throws a cd hook, with a single hook arg being the arguments to the command
|
||||
- Now works for directories with spaces
|
||||
- Lua input now throws a success hook if it succeeded
|
||||
- Command history is now saved to `~/.hilbish-history`
|
||||
- Globals defined in Lua that are strings will be defined as an env variable ([#16](https://github.com/Rosettea/Hilbish/pull/16))
|
||||
- Input ending with `\` will now go to a newline
|
||||
- `exit` command is now written in Lua
|
||||
|
||||
### Fixed
|
||||
|
||||
- Input is now trimmed
|
||||
- Internals are slightly cleaned up and codebase split up
|
||||
- Hilbish will now fall back to a builtin minimal config if the user's config has syntax errors on loading
|
||||
- Commands defined in Lua now report the error to the user cleanly instead of panicking if it has occured
|
||||
|
||||
## [0.2.0] - 2021-03-31
|
||||
|
||||
### Added
|
||||
|
||||
- Hooks (events) are the new and main thing in v0.2, you can now listen for hooks or cast out (emit) custom ones, via the [bait](https://github.com/Hilbis/Hilbish/wiki/Bait) package
|
||||
- `^^` to refer to the previous command. It's for the lazy hands like me, so I can do something like `ENV=VAR ^^`
|
||||
- Way more (more like any) comments in the core code.
|
||||
|
||||
### Changed
|
||||
|
||||
- Prompt has been changed to have printf-like verbs to format. This makes it easier on the user's side to configure, and doesn't require hooks to change it for things like current directory.
|
||||
- Default prompt's changed and the triangle changes color based on if command failed or not.
|
||||
|
||||
## [0.1.2] - 2021-03-24
|
||||
|
||||
### Added
|
||||
|
||||
- Add Lua input to history
|
||||
|
||||
## [0.1.1] - 2021-03-24
|
||||
|
||||
### Added
|
||||
|
||||
- Go to new line if sh input is incomplete
|
||||
|
||||
```bash
|
||||
> for i in {1..5}
|
||||
```
|
||||
|
||||
This input for example will prompt for more input to complete:
|
||||
|
||||

|
||||
|
||||
## [0.1.0] - 2021-03-24
|
||||
|
||||
### Added
|
||||
|
||||
- Tab complete files
|
||||
- Makefile installation
|
||||
- sh support
|
||||
|
||||
## [0.0.12] - 2021-03-21
|
||||
|
||||
First "stable" release of Hilbish.
|
||||
|
||||
[2.3.4]: https://github.com/Rosettea/Hilbish/compare/v2.3.3...v2.3.4
|
||||
[2.3.3]: https://github.com/Rosettea/Hilbish/compare/v2.3.2...v2.3.3
|
||||
[2.3.2]: https://github.com/Rosettea/Hilbish/compare/v2.3.1...v2.3.2
|
||||
[2.3.1]: https://github.com/Rosettea/Hilbish/compare/v2.3.0...v2.3.1
|
||||
[2.3.0]: https://github.com/Rosettea/Hilbish/compare/v2.2.3...v2.3.0
|
||||
[2.2.3]: https://github.com/Rosettea/Hilbish/compare/v2.2.2...v2.2.3
|
||||
[2.2.2]: https://github.com/Rosettea/Hilbish/compare/v2.2.1...v2.2.2
|
||||
[2.2.1]: https://github.com/Rosettea/Hilbish/compare/v2.2.0...v2.2.1
|
||||
[2.2.0]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.2.0
|
||||
[2.1.2]: https://github.com/Rosettea/Hilbish/compare/v2.1.1...v2.1.2
|
||||
[2.1.1]: https://github.com/Rosettea/Hilbish/compare/v2.1.0...v2.1.1
|
||||
[2.1.0]: https://github.com/Rosettea/Hilbish/compare/v2.0.1...v2.1.0
|
||||
[2.0.1]: https://github.com/Rosettea/Hilbish/compare/v2.0.0...v2.0.1
|
||||
[2.0.0]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0
|
||||
[2.0.0-rc1]: https://github.com/Rosettea/Hilbish/compare/v1.2.0...v2.0.0-rc1
|
||||
[1.2.0]: https://github.com/Rosettea/Hilbish/compare/v1.1.4...v1.2.0
|
||||
[1.1.0]: https://github.com/Rosettea/Hilbish/compare/v1.0.4...v1.1.0
|
||||
[1.0.4]: https://github.com/Rosettea/Hilbish/compare/v1.0.3...v1.0.4
|
||||
[1.0.3]: https://github.com/Rosettea/Hilbish/compare/v1.0.2...v1.0.3
|
||||
[1.0.2]: https://github.com/Rosettea/Hilbish/compare/v1.0.1...v1.0.2
|
||||
[1.0.1]: https://github.com/Rosettea/Hilbish/compare/v1.0.0...v1.0.1
|
||||
[1.0.0]: https://github.com/Rosettea/Hilbish/compare/v0.7.1...v1.0.0
|
||||
[0.7.1]: https://github.com/Rosettea/Hilbish/compare/v0.7.0...v0.7.1
|
||||
[0.7.0]: https://github.com/Rosettea/Hilbish/compare/v0.6.1...v0.7.0
|
||||
[0.6.1]: https://github.com/Rosettea/Hilbish/compare/v0.6.0...v0.6.1
|
||||
[0.6.0]: https://github.com/Rosettea/Hilbish/compare/v0.5.1...v0.6.0
|
||||
[0.5.1]: https://github.com/Rosettea/Hilbish/compare/v0.5.0...v0.5.1
|
||||
[0.5.0]: https://github.com/Rosettea/Hilbish/compare/v0.4.0...v0.5.0
|
||||
[0.4.0]: https://github.com/Rosettea/Hilbish/compare/v0.3.2...v0.4.0
|
||||
[0.3.2]: https://github.com/Rosettea/Hilbish/compare/v0.3.1...v0.3.2
|
||||
[0.3.1]: https://github.com/Rosettea/Hilbish/compare/v0.3.0...v0.3.1
|
||||
[0.3.0]: https://github.com/Rosettea/Hilbish/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/Rosettea/Hilbish/compare/v0.1.2...v0.2.0
|
||||
[0.1.2]: https://github.com/Rosettea/Hilbish/compare/v0.1.1...v0.1.2
|
||||
[0.1.1]: https://github.com/Rosettea/Hilbish/compare/v0.1.0...v0.1.1
|
||||
[0.1.0]: https://github.com/Rosettea/Hilbish/compare/v0.0.12...v0.1.0
|
||||
[0.0.12]: https://github.com/Rosettea/Hilbish/releases/tag/v0.0.12
|
||||
@ -10,38 +10,40 @@ a contribution. Be sure to read through it.
|
||||
Use GitHub Issues to report any bugs or to request any features
|
||||
that may be useful to *anyone* else.
|
||||
|
||||
Check [currently open issues](https://github.com/Hilbis/Hilbish/issues)
|
||||
and [closed ones](https://github.com/Hilbis/Hilbish/issues?q=is%3Aissue+is%3Aclosed) to make sure someone else hasn't already made the issue.
|
||||
Check [currently open issues](https://github.com/Rosettea/Hilbish/issues)
|
||||
and [closed ones](https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aclosed)
|
||||
to make sure someone else hasn't already made the issue.
|
||||
|
||||
For bug reports, be sure to include:
|
||||
- Hilbish Version (`hilbish -v`)
|
||||
- Ways to reproduce
|
||||
|
||||
## Code
|
||||
For any code contributions (Lua and/or Go), you should follow these
|
||||
rules:
|
||||
For any code contributions (Lua and/or Go), you should follow these rules:
|
||||
- Tab size 4 indentation
|
||||
- In Lua prefer no braces `()` if the function takes 1 argument
|
||||
- Use camelCase for function names
|
||||
- 80 line column limit, unless it breaks code or anything like that
|
||||
- In Lua prefer no braces `()` if the function takes 1 string argument
|
||||
- Use camelCase
|
||||
|
||||
### Making the Pull Request
|
||||
1. Ensure that any new install or build dependencies are documented in
|
||||
the README.md and PR request.
|
||||
the README.md and pull request.
|
||||
|
||||
2. Say in the pull request details the changes to the shell,
|
||||
this includes useful file locations and breaking changes.
|
||||
2. Mention any and all changes, feature additons, removals, etc. This includes
|
||||
useful file locations and breaking changes. Document them in the [changelog](CHANGELOG.md)
|
||||
in the pull request.
|
||||
|
||||
3. The versioning scheme we use is [SemVer](http://semver.org/) and the
|
||||
commit scheme we use is
|
||||
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
|
||||
Please document any backwards incompatible changes and be sure to name
|
||||
your commits correctly.
|
||||
3. We use [Semver](http://semver.org/) for versioning and
|
||||
[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
for commit messages. Please document any breaking changes and be sure to
|
||||
write proper commits, or your pull request will not be considered.
|
||||
|
||||
4. Finally, make the pull request to the **dev** branch.
|
||||
4. Finally, make the pull request.
|
||||
|
||||
## Finding Issues to Contribute to
|
||||
You can check out the [help wanted](https://github.com/Hilbis/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+)
|
||||
You can check out the [help wanted](https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+)
|
||||
labels to figure out what we need your help working on.
|
||||
|
||||
The [up for grabs](https://github.com/Hilbis/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22+) labeled issues are low hanging fruit that should be
|
||||
easy for anyone. You can use this to get started on contributing!
|
||||
The [up for grabs](https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22+)
|
||||
labeled issues are low hanging fruit that should be easy for anyone. You can
|
||||
use this to get started on contributing!
|
||||
|
||||
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Hilbis Development
|
||||
Copyright (c) 2021-2023 Rosettea
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
34
Makefile
@ -1,34 +0,0 @@
|
||||
PREFIX ?= /usr
|
||||
DESTDIR ?=
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
LIBDIR ?= $(PREFIX)/share/hilbish
|
||||
|
||||
build:
|
||||
@go build -ldflags "-s -w"
|
||||
|
||||
dev:
|
||||
@go build -ldflags "-s -w -X main.version=$(shell git describe --tags)"
|
||||
|
||||
hilbiline:
|
||||
@go build -ldflags "-s -w -X main.version=$(shell git describe --tags)+hilbiline" -tags hilbiline
|
||||
|
||||
install:
|
||||
@install -v -d "$(DESTDIR)$(BINDIR)/" && install -m 0755 -v hilbish "$(DESTDIR)$(BINDIR)/hilbish"
|
||||
@mkdir -p "$(DESTDIR)$(LIBDIR)"
|
||||
@cp libs preload.lua .hilbishrc.lua "$(DESTDIR)$(LIBDIR)" -r
|
||||
@grep "$(DESTDIR)$(BINDIR)/hilbish" -qxF /etc/shells || echo "$(DESTDIR)$(BINDIR)/hilbish" >> /etc/shells
|
||||
@echo "Hilbish Installed"
|
||||
|
||||
uninstall:
|
||||
@rm -vrf \
|
||||
"$(DESTDIR)$(BINDIR)/hilbish" \
|
||||
"$(DESTDIR)$(LIBDIR)"
|
||||
@sed -i '/hilbish/d' /etc/shells
|
||||
@echo "Hilbish Uninstalled"
|
||||
|
||||
clean:
|
||||
@go clean
|
||||
|
||||
all: build install
|
||||
|
||||
.PHONY: install uninstall build dev hilbiline clean
|
||||
160
README.md
@ -1,126 +1,84 @@
|
||||
<div align="center">
|
||||
<h1>Hilbish</h1>
|
||||
<blockquote>
|
||||
🎀 a nice lil shell for lua people made with go and lua
|
||||
</blockquote><p align="center">
|
||||
<a href="https://github.com/Hilbis/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"><img src="https://img.shields.io/github/issues/Hilbis/Hilbish/help%20wanted?color=green" alt="help wanted"></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
|
||||
</p>
|
||||
</div>
|
||||
> [!TIP]
|
||||
> Check out [Hilbish: Midnight Edition](https://github.com/Rosettea/Hilbish/tree/midnight-edition) if you want to use C Lua, LuaJIT or anything related!
|
||||
|
||||
Hilbish is an interactive Unix-like shell written in Go, with the config
|
||||
and other code written in Lua.
|
||||
It is sort of in a stable state currently, usable as a daily shell,
|
||||
but there may still be breaking changes in Lua modules.
|
||||
<img src="./assets/hilbish-logo-and-text.png" width=512><br>
|
||||
<blockquote>
|
||||
🌓 The Moon-powered shell! A comfy and extensible shell for Lua fans! 🌺 ✨
|
||||
</blockquote>
|
||||
|
||||
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/Rosettea/Hilbish?style=flat-square"><img alt="GitHub commits since latest release (by date)" src="https://img.shields.io/github/commits-since/Rosettea/Hilbish/latest?style=flat-square"><img alt="GitHub contributors" src="https://img.shields.io/github/contributors/Rosettea/Hilbish?style=flat-square"><br>
|
||||
<a href="https://github.com/Rosettea/Hilbish/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22"><img src="https://img.shields.io/github/issues/Hilbis/Hilbish/help%20wanted?style=flat-square&color=green" alt="help wanted"></a>
|
||||
<a href="https://github.com/Rosettea/Hilbish/blob/master/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/Rosettea/Hilbish?style=flat-square"></a>
|
||||
<a href="https://discord.gg/3PDdcQz"><img alt="Discord" src="https://img.shields.io/discord/732357621503229962?color=blue&style=flat-square"></a>
|
||||
<br>
|
||||
|
||||
Hilbish is an extensible shell designed to be highly customizable.
|
||||
|
||||
It is configured in Lua, and provides a good range of features.
|
||||
It aims to be easy to use for anyone, and powerful enough for
|
||||
those who need more.
|
||||
|
||||
The motivation for choosing Lua was that its simpler and better to use
|
||||
than old shell scripts. It's fine for basic interactive shell uses,
|
||||
and supports [both Lua and Sh interactively](https://rosettea.github.io/Hilbish/docs/features/runner-mode/).
|
||||
|
||||
That's the only place Hilbish can use traditional shell syntax though;
|
||||
everything else is Lua and aims to be infinitely configurable.
|
||||
|
||||
If something isn't, open an issue!
|
||||
|
||||
# Screenshots
|
||||
<div align="center">
|
||||
<img src="gallery/default.png"><br><br>
|
||||
<img src="gallery/terminal.png"><br><br>
|
||||
<img src="gallery/pillprompt.png">
|
||||
<img src="gallery/tab.png">
|
||||
</div>
|
||||
|
||||
# Links
|
||||
- **[Documentation](https://github.com/Hilbis/Hilbish/wiki)**
|
||||
- **[Gallery](https://github.com/Hilbis/Hilbish/discussions/36)** - See
|
||||
more screenshots of Hilbish in action
|
||||
# Getting Hilbish
|
||||
**NOTE:** Hilbish is not guaranteed to work properly on Windows, starting
|
||||
from the 2.0 version. It will still be able to compile, but functionality
|
||||
may be lacking. If you want to contribute to make the situation better,
|
||||
comment on the Windows discussion.
|
||||
|
||||
# Installation
|
||||
**NOTE:** Hilbish is currently only officially supported and tested on Linux
|
||||
You can check the [install page](https://rosettea.github.io/Hilbish/install/)
|
||||
on the website for distributed binaries from GitHub or other package repositories.
|
||||
Otherwise, continue reading for steps on compiling.
|
||||
|
||||
### Prebuilt binaries
|
||||
Binaries are provided for the latest commit.
|
||||
## Prerequisites
|
||||
- [Go 1.22+](https://go.dev)
|
||||
- [Task](https://taskfile.dev/installation/) (**Go on the hyperlink here to see Task's install method for your OS.**)
|
||||
|
||||
**Note that these use Hilbiline, not readline, and may be missing functionality
|
||||
(moving the cursor, proper unicode support and backspace working properly)**
|
||||
|
||||
Click on the checkmark (or x) near the commit hash, then details for your platform
|
||||
<br><img src="https://modeus.is-inside.me/dyr8UGGq.png"><br>
|
||||
|
||||
Then click on the artifacts drop down, and download artifact for your platform,
|
||||
like what is highlighted in the screenshot.
|
||||
<br><img src="https://modeus.is-inside.me/KJ0Puceb.png"><br>
|
||||
|
||||
### AUR
|
||||
Arch Linux users can install Hilbish from the AUR.
|
||||
## Build
|
||||
First, clone Hilbish. The recursive is required, as some Lua libraries
|
||||
are submodules.
|
||||
```sh
|
||||
yay -S hilbish
|
||||
```
|
||||
If you want the latest and greatest, you can install and compile from the latest git commit
|
||||
```sh
|
||||
yay -S hilbish-git
|
||||
```
|
||||
|
||||
### Manual Build
|
||||
#### Prerequisites
|
||||
- [Go 1.16](https://go.dev)
|
||||
- GNU Readline
|
||||
|
||||
On Fedora, readline can be installed with:
|
||||
```
|
||||
sudo dnf install readline-devel
|
||||
git clone --recursive https://github.com/Rosettea/Hilbish
|
||||
cd Hilbish
|
||||
go get -d ./...
|
||||
```
|
||||
|
||||
On Debian/Ubuntu and distros based on them, it can be installed with:
|
||||
```
|
||||
sudo apt install libreadline-dev
|
||||
To build, run:
|
||||
```
|
||||
task
|
||||
```
|
||||
|
||||
On OpenSUSE, it can be installed with:
|
||||
Or, if you want a stable branch, run these commands:
|
||||
```
|
||||
sudo zypper install readline-devel
|
||||
```
|
||||
|
||||
On Arch Linux, it can be installed with:
|
||||
```
|
||||
sudo pacman -S readline
|
||||
```
|
||||
|
||||
#### Build
|
||||
First, clone Hilbish:
|
||||
```sh
|
||||
git clone --recursive https://github.com/Hilbis/Hilbish
|
||||
cd Hilbish
|
||||
# If you want the latest stable release, run this following command
|
||||
git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
```
|
||||
task build
|
||||
```
|
||||
|
||||
And get dependencies and build:
|
||||
```sh
|
||||
go get -d all
|
||||
make dev
|
||||
```
|
||||
|
||||
If you `git checkout`'d the latest stable release, run
|
||||
`make build` instead of `make dev`.
|
||||
or want to experiment Hilbiline, instead run
|
||||
`make hilbiline`
|
||||
|
||||
#### Install
|
||||
`sudo make install`
|
||||
|
||||
### Uninstall
|
||||
```sh
|
||||
sudo make uninstall
|
||||
```
|
||||
After you did all that, run `sudo task install` to install Hilbish globally.
|
||||
|
||||
# Contributing
|
||||
Any kind of contributions to Hilbish are welcome!
|
||||
Make sure to read [CONTRIBUTING.md](CONTRIBUTING.md) before getting started.
|
||||
Any kind of contributions are welcome! Hilbish is very easy to contribute to.
|
||||
Read [CONTRIBUTING.md](CONTRIBUTING.md) as a guideline to doing so.
|
||||
|
||||
### Special Thanks To
|
||||
Everyone here who has contributed:
|
||||
|
||||
<a href="https://github.com/Hilbis/Hilbish/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=Hilbis/Hilbish" />
|
||||
**Thanks to everyone below who's contributed!**
|
||||
<a href="https://github.com/Rosettea/Hilbish/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=Rosettea/Hilbish" />
|
||||
</a>
|
||||
|
||||
*Made with [contributors-img](https://contrib.rocks).*
|
||||
|
||||
### Credits
|
||||
- [This blog post](https://www.vidarholen.net/contents/blog/?p=878) which
|
||||
is how Hilbish now inserts a newline even if output doesn't have one.
|
||||
|
||||
# License
|
||||
Hilbish is licensed under the MIT license.
|
||||
[Read here](LICENSE) for more info.
|
||||
Hilbish is licensed under the [MIT license](LICENSE).
|
||||
[Images and assets](assets/) are licensed under CC-BY-SA 4.0
|
||||
|
||||
47
Taskfile.yaml
Normal file
@ -0,0 +1,47 @@
|
||||
# https://taskfile.dev
|
||||
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
PREFIX: '{{default "/usr/local" .PREFIX}}'
|
||||
bindir__: '{{.PREFIX}}/bin'
|
||||
BINDIR: '{{default .bindir__ .BINDIR}}'
|
||||
libdir__: ''
|
||||
LIBDIR: '{{default .libdir__ .LIBDIR}}'
|
||||
goflags__: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}}"'
|
||||
GOFLAGS: '{{default .goflags__ .GOFLAGS}}'
|
||||
|
||||
tasks:
|
||||
default:
|
||||
cmds:
|
||||
- go build {{.GOFLAGS}}
|
||||
vars:
|
||||
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
||||
|
||||
default-nocgo:
|
||||
cmds:
|
||||
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||
vars:
|
||||
GOFLAGS: '-ldflags "-s -w -X main.dataDir={{.LIBDIR}} -X main.gitCommit=$(git rev-parse --short HEAD) -X main.gitBranch=$(git rev-parse --abbrev-ref HEAD)"'
|
||||
|
||||
build:
|
||||
cmds:
|
||||
- go build {{.GOFLAGS}}
|
||||
|
||||
build-nocgo:
|
||||
cmds:
|
||||
- CGO_ENABLED=0 go build {{.GOFLAGS}}
|
||||
|
||||
install:
|
||||
cmds:
|
||||
- install -v -d "{{.DESTDIR}}{{.BINDIR}}/" && install -m 0755 -v hilbish "{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
||||
- mkdir -p "{{.DESTDIR}}{{.LIBDIR}}"
|
||||
- cp -r libs docs emmyLuaDocs nature .hilbishrc.lua {{.DESTDIR}}{{.LIBDIR}}
|
||||
- grep -qxF "{{.DESTDIR}}{{.BINDIR}}/hilbish" /etc/shells || echo "{{.DESTDIR}}{{.BINDIR}}/hilbish" >> /etc/shells
|
||||
|
||||
uninstall:
|
||||
cmds:
|
||||
- rm -vrf
|
||||
"{{.DESTDIR}}{{.BINDIR}}/hilbish"
|
||||
"{{.DESTDIR}}{{.LIBDIR}}"
|
||||
- grep -v 'hilbish' /etc/shells > /tmp/shells.hilbish_uninstall && mv /tmp/shells.hilbish_uninstall /etc/shells
|
||||
173
aliases.go
Normal file
@ -0,0 +1,173 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
var aliases *aliasModule
|
||||
|
||||
type aliasModule struct {
|
||||
aliases map[string]string
|
||||
mu *sync.RWMutex
|
||||
}
|
||||
|
||||
// initialize aliases map
|
||||
func newAliases() *aliasModule {
|
||||
return &aliasModule{
|
||||
aliases: make(map[string]string),
|
||||
mu: &sync.RWMutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aliasModule) Add(alias, cmd string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
a.aliases[alias] = cmd
|
||||
}
|
||||
|
||||
func (a *aliasModule) All() map[string]string {
|
||||
return a.aliases
|
||||
}
|
||||
|
||||
func (a *aliasModule) Delete(alias string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
delete(a.aliases, alias)
|
||||
}
|
||||
|
||||
func (a *aliasModule) Resolve(cmdstr string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
|
||||
arg, _ := regexp.Compile(`[\\]?%\d+`)
|
||||
|
||||
args, _ := splitInput(cmdstr)
|
||||
if len(args) == 0 {
|
||||
// this shouldnt reach but...????
|
||||
return cmdstr
|
||||
}
|
||||
|
||||
for a.aliases[args[0]] != "" {
|
||||
alias := a.aliases[args[0]]
|
||||
alias = arg.ReplaceAllStringFunc(alias, func(a string) string {
|
||||
idx, _ := strconv.Atoi(a[1:])
|
||||
if strings.HasPrefix(a, "\\") || idx == 0 {
|
||||
return strings.TrimPrefix(a, "\\")
|
||||
}
|
||||
|
||||
if idx + 1 > len(args) {
|
||||
return a
|
||||
}
|
||||
val := args[idx]
|
||||
args = cut(args, idx)
|
||||
cmdstr = strings.Join(args, " ")
|
||||
|
||||
return val
|
||||
})
|
||||
|
||||
cmdstr = alias + strings.TrimPrefix(cmdstr, args[0])
|
||||
cmdArgs, _ := splitInput(cmdstr)
|
||||
args = cmdArgs
|
||||
|
||||
if a.aliases[args[0]] == alias {
|
||||
break
|
||||
}
|
||||
if a.aliases[args[0]] != "" {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return cmdstr
|
||||
}
|
||||
|
||||
// lua section
|
||||
|
||||
// #interface aliases
|
||||
// command aliasing
|
||||
// The alias interface deals with all command aliases in Hilbish.
|
||||
func (a *aliasModule) Loader(rtm *rt.Runtime) *rt.Table {
|
||||
// create a lua module with our functions
|
||||
hshaliasesLua := map[string]util.LuaExport{
|
||||
"add": util.LuaExport{hlalias, 2, false},
|
||||
"list": util.LuaExport{a.luaList, 0, false},
|
||||
"del": util.LuaExport{a.luaDelete, 1, false},
|
||||
"resolve": util.LuaExport{a.luaResolve, 1, false},
|
||||
}
|
||||
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, hshaliasesLua)
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
// #interface aliases
|
||||
// add(alias, cmd)
|
||||
// This is an alias (ha) for the [hilbish.alias](../#alias) function.
|
||||
// --- @param alias string
|
||||
// --- @param cmd string
|
||||
func _hlalias() {}
|
||||
|
||||
// #interface aliases
|
||||
// list() -> table[string, string]
|
||||
// Get a table of all aliases, with string keys as the alias and the value as the command.
|
||||
// #returns table[string, string]
|
||||
/*
|
||||
#example
|
||||
hilbish.aliases.add('hi', 'echo hi')
|
||||
|
||||
local aliases = hilbish.aliases.list()
|
||||
-- -> {hi = 'echo hi'}
|
||||
#example
|
||||
*/
|
||||
func (a *aliasModule) luaList(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
aliasesList := rt.NewTable()
|
||||
for k, v := range a.All() {
|
||||
aliasesList.Set(rt.StringValue(k), rt.StringValue(v))
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(aliasesList)), nil
|
||||
}
|
||||
|
||||
// #interface aliases
|
||||
// delete(name)
|
||||
// Removes an alias.
|
||||
// #param name string
|
||||
func (a *aliasModule) luaDelete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alias, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.Delete(alias)
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// #interface aliases
|
||||
// resolve(alias) -> string?
|
||||
// Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.
|
||||
// #param alias string
|
||||
// #returns string
|
||||
func (a *aliasModule) luaResolve(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alias, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolved := a.Resolve(alias)
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(resolved)), nil
|
||||
}
|
||||
687
api.go
Normal file
@ -0,0 +1,687 @@
|
||||
// the core Hilbish API
|
||||
// The Hilbish module includes the core API, containing
|
||||
// interfaces and functions which directly relate to shell functionality.
|
||||
// #field ver The version of Hilbish
|
||||
// #field goVersion The version of Go that Hilbish was compiled with
|
||||
// #field user Username of the user
|
||||
// #field host Hostname of the machine
|
||||
// #field dataDir Directory for Hilbish data files, including the docs and default modules
|
||||
// #field interactive Is Hilbish in an interactive shell?
|
||||
// #field login Is Hilbish the login shell?
|
||||
// #field vimMode Current Vim input mode of Hilbish (will be nil if not in Vim input mode)
|
||||
// #field exitCode Exit code of the last executed command
|
||||
package main
|
||||
|
||||
import (
|
||||
//"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
"github.com/arnodel/golua/lib/packagelib"
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
|
||||
//"github.com/arnodel/golua/lib/iolib"
|
||||
"github.com/maxlandon/readline"
|
||||
//"mvdan.cc/sh/v3/interp"
|
||||
)
|
||||
|
||||
var exports = map[string]util.LuaExport{
|
||||
"alias": {hlalias, 2, false},
|
||||
"appendPath": {hlappendPath, 1, false},
|
||||
"complete": {hlcomplete, 2, false},
|
||||
"cwd": {hlcwd, 0, false},
|
||||
"exec": {hlexec, 1, false},
|
||||
"goro": {hlgoro, 1, true},
|
||||
"highlighter": {hlhighlighter, 1, false},
|
||||
"hinter": {hlhinter, 1, false},
|
||||
"multiprompt": {hlmultiprompt, 1, false},
|
||||
"prependPath": {hlprependPath, 1, false},
|
||||
"prompt": {hlprompt, 1, true},
|
||||
"inputMode": {hlinputMode, 1, false},
|
||||
"interval": {hlinterval, 2, false},
|
||||
"read": {hlread, 1, false},
|
||||
"timeout": {hltimeout, 2, false},
|
||||
"which": {hlwhich, 1, false},
|
||||
}
|
||||
|
||||
var hshMod *rt.Table
|
||||
var hilbishLoader = packagelib.Loader{
|
||||
Load: hilbishLoad,
|
||||
Name: "hilbish",
|
||||
}
|
||||
|
||||
func hilbishLoad(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
mod := rt.NewTable()
|
||||
|
||||
util.SetExports(rtm, mod, exports)
|
||||
if hshMod == nil {
|
||||
hshMod = mod
|
||||
}
|
||||
|
||||
host, _ := os.Hostname()
|
||||
username := curuser.Username
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
username = strings.Split(username, "\\")[1] // for some reason Username includes the hostname on windows
|
||||
}
|
||||
|
||||
util.SetField(rtm, mod, "ver", rt.StringValue(getVersion()))
|
||||
util.SetField(rtm, mod, "goVersion", rt.StringValue(runtime.Version()))
|
||||
util.SetField(rtm, mod, "user", rt.StringValue(username))
|
||||
util.SetField(rtm, mod, "host", rt.StringValue(host))
|
||||
util.SetField(rtm, mod, "home", rt.StringValue(curuser.HomeDir))
|
||||
util.SetField(rtm, mod, "dataDir", rt.StringValue(dataDir))
|
||||
util.SetField(rtm, mod, "interactive", rt.BoolValue(interactive))
|
||||
util.SetField(rtm, mod, "login", rt.BoolValue(login))
|
||||
util.SetField(rtm, mod, "vimMode", rt.NilValue)
|
||||
util.SetField(rtm, mod, "exitCode", rt.IntValue(0))
|
||||
|
||||
// hilbish.userDir table
|
||||
hshuser := userDirLoader(rtm)
|
||||
mod.Set(rt.StringValue("userDir"), rt.TableValue(hshuser))
|
||||
|
||||
// hilbish.os table
|
||||
hshos := hshosLoader(rtm)
|
||||
mod.Set(rt.StringValue("os"), rt.TableValue(hshos))
|
||||
|
||||
// hilbish.aliases table
|
||||
aliases = newAliases()
|
||||
aliasesModule := aliases.Loader(rtm)
|
||||
mod.Set(rt.StringValue("aliases"), rt.TableValue(aliasesModule))
|
||||
|
||||
// hilbish.history table
|
||||
historyModule := lr.Loader(rtm)
|
||||
mod.Set(rt.StringValue("history"), rt.TableValue(historyModule))
|
||||
|
||||
// hilbish.completion table
|
||||
hshcomp := completionLoader(rtm)
|
||||
// TODO: REMOVE "completion" AND ONLY USE "completions" WITH AN S
|
||||
mod.Set(rt.StringValue("completions"), rt.TableValue(hshcomp))
|
||||
|
||||
// hilbish.runner table
|
||||
runnerModule := runnerModeLoader(rtm)
|
||||
mod.Set(rt.StringValue("runner"), rt.TableValue(runnerModule))
|
||||
|
||||
// hilbish.jobs table
|
||||
jobs = newJobHandler()
|
||||
jobModule := jobs.loader(rtm)
|
||||
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
|
||||
|
||||
// hilbish.timers table
|
||||
timers = newTimersModule()
|
||||
timersModule := timers.loader(rtm)
|
||||
mod.Set(rt.StringValue("timers"), rt.TableValue(timersModule))
|
||||
|
||||
versionModule := rt.NewTable()
|
||||
util.SetField(rtm, versionModule, "branch", rt.StringValue(gitBranch))
|
||||
util.SetField(rtm, versionModule, "full", rt.StringValue(getVersion()))
|
||||
util.SetField(rtm, versionModule, "commit", rt.StringValue(gitCommit))
|
||||
util.SetField(rtm, versionModule, "release", rt.StringValue(releaseName))
|
||||
mod.Set(rt.StringValue("version"), rt.TableValue(versionModule))
|
||||
|
||||
pluginModule := moduleLoader(rtm)
|
||||
mod.Set(rt.StringValue("module"), rt.TableValue(pluginModule))
|
||||
|
||||
sinkModule := util.SinkLoader(rtm)
|
||||
mod.Set(rt.StringValue("sink"), rt.TableValue(sinkModule))
|
||||
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
func getenv(key, fallback string) string {
|
||||
value := os.Getenv(key)
|
||||
if len(value) == 0 {
|
||||
return fallback
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func setVimMode(mode string) {
|
||||
util.SetField(l, hshMod, "vimMode", rt.StringValue(mode))
|
||||
hooks.Emit("hilbish.vimMode", mode)
|
||||
}
|
||||
|
||||
func unsetVimMode() {
|
||||
util.SetField(l, hshMod, "vimMode", rt.NilValue)
|
||||
}
|
||||
|
||||
/*
|
||||
func handleStream(v rt.Value, strms *streams, errStream bool) error {
|
||||
ud, ok := v.TryUserData()
|
||||
if !ok {
|
||||
return errors.New("expected metatable argument")
|
||||
}
|
||||
|
||||
val := ud.Value()
|
||||
var varstrm io.Writer
|
||||
if f, ok := val.(*iolib.File); ok {
|
||||
varstrm = f.Handle()
|
||||
}
|
||||
|
||||
if f, ok := val.(*sink); ok {
|
||||
varstrm = f.writer
|
||||
}
|
||||
|
||||
if varstrm == nil {
|
||||
return errors.New("expected either a sink or file")
|
||||
}
|
||||
|
||||
if errStream {
|
||||
strms.stderr = varstrm
|
||||
} else {
|
||||
strms.stdout = varstrm
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
// cwd() -> string
|
||||
// Returns the current directory of the shell.
|
||||
// #returns string
|
||||
func hlcwd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
cwd, _ := os.Getwd()
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(cwd)), nil
|
||||
}
|
||||
|
||||
// read(prompt) -> input (string)
|
||||
// Read input from the user, using Hilbish's line editor/input reader.
|
||||
// This is a separate instance from the one Hilbish actually uses.
|
||||
// Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||
// #param prompt? string Text to print before input, can be empty.
|
||||
// #returns string|nil
|
||||
func hlread(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
luaprompt := c.Arg(0)
|
||||
if typ := luaprompt.Type(); typ != rt.StringType && typ != rt.NilType {
|
||||
return nil, errors.New("expected #1 to be a string")
|
||||
}
|
||||
prompt, ok := luaprompt.TryString()
|
||||
if !ok {
|
||||
// if we are here and `luaprompt` is not a string, it's nil
|
||||
// substitute with an empty string
|
||||
prompt = ""
|
||||
}
|
||||
|
||||
lualr := &lineReader{
|
||||
rl: readline.NewInstance(),
|
||||
}
|
||||
lualr.SetPrompt(prompt)
|
||||
|
||||
input, err := lualr.Read()
|
||||
if err != nil {
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(input)), nil
|
||||
}
|
||||
|
||||
/*
|
||||
prompt(str, typ)
|
||||
Changes the shell prompt to the provided string.
|
||||
There are a few verbs that can be used in the prompt text.
|
||||
These will be formatted and replaced with the appropriate values.
|
||||
`%d` - Current working directory
|
||||
`%u` - Name of current user
|
||||
`%h` - Hostname of device
|
||||
#param str string
|
||||
#param typ? string Type of prompt, being left or right. Left by default.
|
||||
#example
|
||||
-- the default hilbish prompt without color
|
||||
hilbish.prompt '%u %d ∆'
|
||||
-- or something of old:
|
||||
hilbish.prompt '%u@%h :%d $'
|
||||
-- prompt: user@hostname: ~/directory $
|
||||
#example
|
||||
*/
|
||||
func hlprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
err := c.Check1Arg()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ := "left"
|
||||
// optional 2nd arg
|
||||
if len(c.Etc()) != 0 {
|
||||
ltyp := c.Etc()[0]
|
||||
var ok bool
|
||||
typ, ok = ltyp.TryString()
|
||||
if !ok {
|
||||
return nil, errors.New("bad argument to run (expected string, got " + ltyp.TypeName() + ")")
|
||||
}
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case "left":
|
||||
prompt = p
|
||||
lr.SetPrompt(fmtPrompt(prompt))
|
||||
case "right":
|
||||
lr.SetRightPrompt(fmtPrompt(p))
|
||||
default:
|
||||
return nil, errors.New("expected prompt type to be right or left, got " + typ)
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// multiprompt(str)
|
||||
// Changes the text prompt when Hilbish asks for more input.
|
||||
// This will show up when text is incomplete, like a missing quote
|
||||
// #param str string
|
||||
/*
|
||||
#example
|
||||
--[[
|
||||
imagine this is your text input:
|
||||
user ~ ∆ echo "hey
|
||||
|
||||
but there's a missing quote! hilbish will now prompt you so the terminal
|
||||
will look like:
|
||||
user ~ ∆ echo "hey
|
||||
--> ...!"
|
||||
|
||||
so then you get
|
||||
user ~ ∆ echo "hey
|
||||
--> ...!"
|
||||
hey ...!
|
||||
]]--
|
||||
hilbish.multiprompt '-->'
|
||||
#example
|
||||
*/
|
||||
func hlmultiprompt(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(multilinePrompt)), nil
|
||||
}
|
||||
prompt, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
multilinePrompt = prompt
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// alias(cmd, orig)
|
||||
// Sets an alias, with a name of `cmd` to another command.
|
||||
// #param cmd string Name of the alias
|
||||
// #param orig string Command that will be aliased
|
||||
/*
|
||||
#example
|
||||
-- With this, "ga file" will turn into "git add file"
|
||||
hilbish.alias('ga', 'git add')
|
||||
|
||||
-- Numbered substitutions are supported here!
|
||||
hilbish.alias('dircount', 'ls %1 | wc -l')
|
||||
-- "dircount ~" would count how many files are in ~ (home directory).
|
||||
#example
|
||||
*/
|
||||
func hlalias(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
orig, err := c.StringArg(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aliases.Add(cmd, orig)
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// appendPath(dir)
|
||||
// Appends the provided dir to the command path (`$PATH`)
|
||||
// #param dir string|table Directory (or directories) to append to path
|
||||
/*
|
||||
#example
|
||||
hilbish.appendPath '~/go/bin'
|
||||
-- Will add ~/go/bin to the command path.
|
||||
|
||||
-- Or do multiple:
|
||||
hilbish.appendPath {
|
||||
'~/go/bin',
|
||||
'~/.local/bin'
|
||||
}
|
||||
#example
|
||||
*/
|
||||
func hlappendPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arg := c.Arg(0)
|
||||
|
||||
// check if dir is a table or a string
|
||||
if arg.Type() == rt.TableType {
|
||||
util.ForEach(arg.AsTable(), func(k rt.Value, v rt.Value) {
|
||||
if v.Type() == rt.StringType {
|
||||
appendPath(v.AsString())
|
||||
}
|
||||
})
|
||||
} else if arg.Type() == rt.StringType {
|
||||
appendPath(arg.AsString())
|
||||
} else {
|
||||
return nil, errors.New("bad argument to appendPath (expected string or table, got " + arg.TypeName() + ")")
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
func appendPath(dir string) {
|
||||
dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
|
||||
pathenv := os.Getenv("PATH")
|
||||
|
||||
// if dir isnt already in $PATH, add it
|
||||
if !strings.Contains(pathenv, dir) {
|
||||
os.Setenv("PATH", pathenv+string(os.PathListSeparator)+dir)
|
||||
}
|
||||
}
|
||||
|
||||
// exec(cmd)
|
||||
// Replaces the currently running Hilbish instance with the supplied command.
|
||||
// This can be used to do an in-place restart.
|
||||
// #param cmd string
|
||||
func hlexec(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmdArgs, _ := splitInput(cmd)
|
||||
if runtime.GOOS != "windows" {
|
||||
cmdPath, err := util.LookPath(cmdArgs[0])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
// if we get here, cmdPath will be nothing
|
||||
// therefore nothing will run
|
||||
}
|
||||
|
||||
// syscall.Exec requires an absolute path to a binary
|
||||
// path, args, string slice of environments
|
||||
syscall.Exec(cmdPath, cmdArgs, os.Environ())
|
||||
} else {
|
||||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Run()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// goro(fn)
|
||||
// Puts `fn` in a Goroutine.
|
||||
// This can be used to run any function in another thread at the same time as other Lua code.
|
||||
// **NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.**
|
||||
// **This is a limitation of the Lua runtime.**
|
||||
// #param fn function
|
||||
func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fn, err := c.ClosureArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// call fn
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// do something here?
|
||||
}
|
||||
}()
|
||||
|
||||
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(fn), c.Etc()...)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error in goro function:\n\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// timeout(cb, time) -> @Timer
|
||||
// Executed the `cb` function after a period of `time`.
|
||||
// This creates a Timer that starts ticking immediately.
|
||||
// #param cb function
|
||||
// #param time number Time to run in milliseconds.
|
||||
// #returns Timer
|
||||
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cb, err := c.ClosureArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ms, err := c.IntArg(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
interval := time.Duration(ms) * time.Millisecond
|
||||
timer := timers.create(timerTimeout, interval, cb)
|
||||
timer.start()
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
|
||||
}
|
||||
|
||||
// interval(cb, time) -> @Timer
|
||||
// Runs the `cb` function every specified amount of `time`.
|
||||
// This creates a timer that ticking immediately.
|
||||
// #param cb function
|
||||
// #param time number Time in milliseconds.
|
||||
// #return Timer
|
||||
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cb, err := c.ClosureArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ms, err := c.IntArg(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
interval := time.Duration(ms) * time.Millisecond
|
||||
timer := timers.create(timerInterval, interval, cb)
|
||||
timer.start()
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
|
||||
}
|
||||
|
||||
// complete(scope, cb)
|
||||
// Registers a completion handler for the specified scope.
|
||||
// A `scope` is expected to be `command.<cmd>`,
|
||||
// replacing <cmd> with the name of the command (for example `command.git`).
|
||||
// The documentation for completions, under Features/Completions or `doc completions`
|
||||
// provides more details.
|
||||
// #param scope string
|
||||
// #param cb function
|
||||
/*
|
||||
#example
|
||||
-- This is a very simple example. Read the full doc for completions for details.
|
||||
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||
if #fields == 0 then
|
||||
-- complete for commands
|
||||
local comps, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||
local compGroup = {
|
||||
items = comps, -- our list of items to complete
|
||||
type = 'grid' -- what our completions will look like.
|
||||
}
|
||||
|
||||
return {compGroup}, pfx
|
||||
end
|
||||
|
||||
-- otherwise just be boring and return files
|
||||
|
||||
local comps, pfx = hilbish.completion.files(query, ctx, fields)
|
||||
local compGroup = {
|
||||
items = comps,
|
||||
type = 'grid'
|
||||
}
|
||||
|
||||
return {compGroup}, pfx
|
||||
end)
|
||||
#example
|
||||
*/
|
||||
func hlcomplete(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
scope, cb, err := util.HandleStrCallback(t, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
luaCompletions[scope] = cb
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// prependPath(dir)
|
||||
// Prepends `dir` to $PATH.
|
||||
// #param dir string
|
||||
func hlprependPath(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir = strings.Replace(dir, "~", curuser.HomeDir, 1)
|
||||
pathenv := os.Getenv("PATH")
|
||||
|
||||
// if dir isnt already in $PATH, add in
|
||||
if !strings.Contains(pathenv, dir) {
|
||||
os.Setenv("PATH", dir+string(os.PathListSeparator)+pathenv)
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// which(name) -> string
|
||||
// Checks if `name` is a valid command.
|
||||
// Will return the path of the binary, or a basename if it's a commander.
|
||||
// #param name string
|
||||
// #returns string
|
||||
func hlwhich(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// itll return either the original command or what was passed
|
||||
// if name isnt empty its not an issue
|
||||
alias := aliases.Resolve(name)
|
||||
cmd := strings.Split(alias, " ")[0]
|
||||
|
||||
// check for commander
|
||||
if cmds.Commands[cmd] != nil {
|
||||
// they dont resolve to a path, so just send the cmd
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(cmd)), nil
|
||||
}
|
||||
|
||||
path, err := util.LookPath(cmd)
|
||||
if err != nil {
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(path)), nil
|
||||
}
|
||||
|
||||
// inputMode(mode)
|
||||
// Sets the input mode for Hilbish's line reader.
|
||||
// `emacs` is the default. Setting it to `vim` changes behavior of input to be
|
||||
// Vim-like with modes and Vim keybinds.
|
||||
// #param mode string Can be set to either `emacs` or `vim`
|
||||
func hlinputMode(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case "emacs":
|
||||
unsetVimMode()
|
||||
lr.rl.InputMode = readline.Emacs
|
||||
case "vim":
|
||||
setVimMode("insert")
|
||||
lr.rl.InputMode = readline.Vim
|
||||
default:
|
||||
return nil, errors.New("inputMode: expected vim or emacs, received " + mode)
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// hinter(line, pos)
|
||||
// The command line hint handler. It gets called on every key insert to
|
||||
// determine what text to use as an inline hint. It is passed the current
|
||||
// line and cursor position. It is expected to return a string which is used
|
||||
// as the text for the hint. This is by default a shim. To set hints,
|
||||
// override this function with your custom handler.
|
||||
// #param line string
|
||||
// #param pos number Position of cursor in line. Usually equals string.len(line)
|
||||
/*
|
||||
#example
|
||||
-- this will display "hi" after the cursor in a dimmed color.
|
||||
function hilbish.hinter(line, pos)
|
||||
return 'hi'
|
||||
end
|
||||
#example
|
||||
*/
|
||||
func hlhinter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// highlighter(line)
|
||||
// Line highlighter handler.
|
||||
// This is mainly for syntax highlighting, but in reality could set the input
|
||||
// of the prompt to *display* anything. The callback is passed the current line
|
||||
// and is expected to return a line that will be used as the input display.
|
||||
// Note that to set a highlighter, one has to override this function.
|
||||
// #example
|
||||
// --This code will highlight all double quoted strings in green.
|
||||
// function hilbish.highlighter(line)
|
||||
//
|
||||
// return line:gsub('"%w+"', function(c) return lunacolors.green(c) end)
|
||||
//
|
||||
// end
|
||||
// #example
|
||||
// #param line string
|
||||
func hlhighlighter(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(line)), nil
|
||||
}
|
||||
BIN
assets/hilbish-flower.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
assets/hilbish-logo-and-text.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
714
cmd/docgen/docgen.go
Normal file
@ -0,0 +1,714 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/doc"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
md "github.com/atsushinee/go-markdown-generator/doc"
|
||||
)
|
||||
|
||||
var header = `---
|
||||
title: %s %s
|
||||
description: %s
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
`
|
||||
|
||||
type emmyPiece struct {
|
||||
DocPiece *docPiece
|
||||
Annotations []string
|
||||
Params []string // we only need to know param name to put in function
|
||||
FuncName string
|
||||
}
|
||||
|
||||
type module struct {
|
||||
Types []docPiece
|
||||
Docs []docPiece
|
||||
Fields []docPiece
|
||||
Properties []docPiece
|
||||
ShortDescription string
|
||||
Description string
|
||||
ParentModule string
|
||||
HasInterfaces bool
|
||||
HasTypes bool
|
||||
}
|
||||
|
||||
type param struct {
|
||||
Name string
|
||||
Type string
|
||||
Doc []string
|
||||
}
|
||||
|
||||
type docPiece struct {
|
||||
Doc []string
|
||||
FuncSig string
|
||||
FuncName string
|
||||
Interfacing string
|
||||
ParentModule string
|
||||
GoFuncName string
|
||||
IsInterface bool
|
||||
IsMember bool
|
||||
IsType bool
|
||||
Fields []docPiece
|
||||
Properties []docPiece
|
||||
Params []param
|
||||
Tags map[string][]tag
|
||||
}
|
||||
|
||||
type tag struct {
|
||||
id string
|
||||
fields []string
|
||||
startIdx int
|
||||
}
|
||||
|
||||
var docs = make(map[string]module)
|
||||
var interfaceDocs = make(map[string]module)
|
||||
var emmyDocs = make(map[string][]emmyPiece)
|
||||
var typeTable = make(map[string][]string) // [0] = parentMod, [1] = interfaces
|
||||
var prefix = map[string]string{
|
||||
"main": "hl",
|
||||
"hilbish": "hl",
|
||||
"fs": "f",
|
||||
"commander": "c",
|
||||
"bait": "b",
|
||||
"terminal": "term",
|
||||
"snail": "snail",
|
||||
"readline": "rl",
|
||||
"yarn": "yarn",
|
||||
}
|
||||
|
||||
func getTagsAndDocs(docs string) (map[string][]tag, []string) {
|
||||
pts := strings.Split(docs, "\n")
|
||||
parts := []string{}
|
||||
tags := make(map[string][]tag)
|
||||
|
||||
for idx, part := range pts {
|
||||
if strings.HasPrefix(part, "#") {
|
||||
tagParts := strings.Split(strings.TrimPrefix(part, "#"), " ")
|
||||
if tags[tagParts[0]] == nil {
|
||||
var id string
|
||||
if len(tagParts) > 1 {
|
||||
id = tagParts[1]
|
||||
}
|
||||
tags[tagParts[0]] = []tag{
|
||||
{id: id, startIdx: idx},
|
||||
}
|
||||
if len(tagParts) >= 2 {
|
||||
tags[tagParts[0]][0].fields = tagParts[2:]
|
||||
}
|
||||
} else {
|
||||
if tagParts[0] == "example" {
|
||||
exampleIdx := tags["example"][0].startIdx
|
||||
exampleCode := pts[exampleIdx+1 : idx]
|
||||
|
||||
tags["example"][0].fields = exampleCode
|
||||
parts = strings.Split(strings.Replace(strings.Join(parts, "\n"), strings.TrimPrefix(strings.Join(exampleCode, "\n"), "#example\n"), "", -1), "\n")
|
||||
continue
|
||||
}
|
||||
|
||||
fleds := []string{}
|
||||
if len(tagParts) >= 2 {
|
||||
fleds = tagParts[2:]
|
||||
}
|
||||
tags[tagParts[0]] = append(tags[tagParts[0]], tag{
|
||||
id: tagParts[1],
|
||||
fields: fleds,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
parts = append(parts, part)
|
||||
}
|
||||
}
|
||||
|
||||
return tags, parts
|
||||
}
|
||||
|
||||
func docPieceTag(tagName string, tags map[string][]tag) []docPiece {
|
||||
dps := []docPiece{}
|
||||
for _, tag := range tags[tagName] {
|
||||
dps = append(dps, docPiece{
|
||||
FuncName: tag.id,
|
||||
Doc: tag.fields,
|
||||
})
|
||||
}
|
||||
|
||||
return dps
|
||||
}
|
||||
|
||||
func setupDocType(mod string, typ *doc.Type) *docPiece {
|
||||
docs := strings.TrimSpace(typ.Doc)
|
||||
tags, doc := getTagsAndDocs(docs)
|
||||
|
||||
if tags["type"] == nil {
|
||||
return nil
|
||||
}
|
||||
inInterface := tags["interface"] != nil
|
||||
|
||||
var interfaces string
|
||||
typeName := strings.ToUpper(string(typ.Name[0])) + typ.Name[1:]
|
||||
typeDoc := []string{}
|
||||
|
||||
if inInterface {
|
||||
interfaces = tags["interface"][0].id
|
||||
}
|
||||
|
||||
fields := docPieceTag("field", tags)
|
||||
properties := docPieceTag("property", tags)
|
||||
|
||||
for _, d := range doc {
|
||||
if strings.HasPrefix(d, "---") {
|
||||
// TODO: document types in lua
|
||||
/*
|
||||
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
|
||||
emmyLinePieces := strings.Split(emmyLine, " ")
|
||||
emmyType := emmyLinePieces[0]
|
||||
if emmyType == "@param" {
|
||||
em.Params = append(em.Params, emmyLinePieces[1])
|
||||
}
|
||||
if emmyType == "@vararg" {
|
||||
em.Params = append(em.Params, "...") // add vararg
|
||||
}
|
||||
em.Annotations = append(em.Annotations, d)
|
||||
*/
|
||||
} else {
|
||||
typeDoc = append(typeDoc, d)
|
||||
}
|
||||
}
|
||||
|
||||
var isMember bool
|
||||
if tags["member"] != nil {
|
||||
isMember = true
|
||||
}
|
||||
parentMod := mod
|
||||
dps := &docPiece{
|
||||
Doc: typeDoc,
|
||||
FuncName: typeName,
|
||||
Interfacing: interfaces,
|
||||
IsInterface: inInterface,
|
||||
IsMember: isMember,
|
||||
IsType: true,
|
||||
ParentModule: parentMod,
|
||||
Fields: fields,
|
||||
Properties: properties,
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
typeTable[strings.ToLower(typeName)] = []string{parentMod, interfaces}
|
||||
|
||||
return dps
|
||||
}
|
||||
|
||||
func setupDoc(mod string, fun *doc.Func) *docPiece {
|
||||
if fun.Doc == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
docs := strings.TrimSpace(fun.Doc)
|
||||
tags, parts := getTagsAndDocs(docs)
|
||||
|
||||
// i couldnt fit this into the condition below for some reason so here's a goto!
|
||||
if tags["member"] != nil {
|
||||
goto start
|
||||
}
|
||||
|
||||
if prefix[mod] == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if (!strings.HasPrefix(fun.Name, prefix[mod]) && tags["interface"] == nil) || (strings.ToLower(fun.Name) == "loader" && tags["interface"] == nil) {
|
||||
return nil
|
||||
}
|
||||
|
||||
start:
|
||||
inInterface := tags["interface"] != nil
|
||||
var interfaces string
|
||||
funcsig := parts[0]
|
||||
doc := parts[1:]
|
||||
funcName := strings.TrimPrefix(fun.Name, prefix[mod])
|
||||
funcdoc := []string{}
|
||||
|
||||
if inInterface {
|
||||
interfaces = tags["interface"][0].id
|
||||
funcName = interfaces + "." + strings.Split(funcsig, "(")[0]
|
||||
}
|
||||
em := emmyPiece{FuncName: funcName}
|
||||
|
||||
fields := docPieceTag("field", tags)
|
||||
properties := docPieceTag("property", tags)
|
||||
var params []param
|
||||
if paramsRaw := tags["param"]; paramsRaw != nil {
|
||||
params = make([]param, len(paramsRaw))
|
||||
for i, p := range paramsRaw {
|
||||
params[i] = param{
|
||||
Name: p.id,
|
||||
Type: p.fields[0],
|
||||
Doc: p.fields[1:],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, d := range doc {
|
||||
if strings.HasPrefix(d, "---") {
|
||||
emmyLine := strings.TrimSpace(strings.TrimPrefix(d, "---"))
|
||||
emmyLinePieces := strings.Split(emmyLine, " ")
|
||||
emmyType := emmyLinePieces[0]
|
||||
if emmyType == "@param" {
|
||||
em.Params = append(em.Params, emmyLinePieces[1])
|
||||
}
|
||||
if emmyType == "@vararg" {
|
||||
em.Params = append(em.Params, "...") // add vararg
|
||||
}
|
||||
em.Annotations = append(em.Annotations, d)
|
||||
} else {
|
||||
funcdoc = append(funcdoc, d)
|
||||
}
|
||||
}
|
||||
|
||||
var isMember bool
|
||||
if tags["member"] != nil {
|
||||
isMember = true
|
||||
}
|
||||
var parentMod string
|
||||
if inInterface {
|
||||
parentMod = mod
|
||||
}
|
||||
dps := &docPiece{
|
||||
Doc: funcdoc,
|
||||
FuncSig: funcsig,
|
||||
FuncName: funcName,
|
||||
Interfacing: interfaces,
|
||||
GoFuncName: strings.ToLower(fun.Name),
|
||||
IsInterface: inInterface,
|
||||
IsMember: isMember,
|
||||
ParentModule: parentMod,
|
||||
Fields: fields,
|
||||
Properties: properties,
|
||||
Params: params,
|
||||
Tags: tags,
|
||||
}
|
||||
if strings.HasSuffix(dps.GoFuncName, strings.ToLower("loader")) {
|
||||
dps.Doc = parts
|
||||
}
|
||||
em.DocPiece = dps
|
||||
|
||||
emmyDocs[mod] = append(emmyDocs[mod], em)
|
||||
return dps
|
||||
}
|
||||
|
||||
func main() {
|
||||
fset := token.NewFileSet()
|
||||
os.Mkdir("docs", 0777)
|
||||
os.RemoveAll("docs/api")
|
||||
os.Mkdir("docs/api", 0777)
|
||||
|
||||
f, err := os.Create("docs/api/_index.md")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f.WriteString(`---
|
||||
title: API
|
||||
layout: doc
|
||||
weight: -100
|
||||
menu: docs
|
||||
---
|
||||
|
||||
Welcome to the API documentation for Hilbish. This documents Lua functions
|
||||
provided by Hilbish.
|
||||
`)
|
||||
f.Close()
|
||||
|
||||
os.Mkdir("emmyLuaDocs", 0777)
|
||||
|
||||
dirs := []string{"./", "./util"}
|
||||
filepath.Walk("golibs/", func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
dirs = append(dirs, "./"+path)
|
||||
return nil
|
||||
})
|
||||
|
||||
pkgs := make(map[string]*ast.Package)
|
||||
for _, path := range dirs {
|
||||
d, err := parser.ParseDir(fset, path, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
for k, v := range d {
|
||||
pkgs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
interfaceModules := make(map[string]*module)
|
||||
for l, f := range pkgs {
|
||||
p := doc.New(f, "./", doc.AllDecls)
|
||||
pieces := []docPiece{}
|
||||
typePieces := []docPiece{}
|
||||
mod := l
|
||||
if mod == "main" || mod == "util" {
|
||||
mod = "hilbish"
|
||||
}
|
||||
var hasInterfaces bool
|
||||
for _, t := range p.Funcs {
|
||||
piece := setupDoc(mod, t)
|
||||
if piece == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
pieces = append(pieces, *piece)
|
||||
if piece.IsInterface {
|
||||
hasInterfaces = true
|
||||
}
|
||||
}
|
||||
for _, t := range p.Types {
|
||||
typePiece := setupDocType(mod, t)
|
||||
if typePiece != nil {
|
||||
typePieces = append(typePieces, *typePiece)
|
||||
if typePiece.IsInterface {
|
||||
hasInterfaces = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range t.Methods {
|
||||
piece := setupDoc(mod, m)
|
||||
if piece == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
pieces = append(pieces, *piece)
|
||||
if piece.IsInterface {
|
||||
hasInterfaces = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags, descParts := getTagsAndDocs(strings.TrimSpace(p.Doc))
|
||||
shortDesc := descParts[0]
|
||||
desc := descParts[1:]
|
||||
filteredPieces := []docPiece{}
|
||||
filteredTypePieces := []docPiece{}
|
||||
for _, piece := range pieces {
|
||||
if !piece.IsInterface {
|
||||
filteredPieces = append(filteredPieces, piece)
|
||||
continue
|
||||
}
|
||||
|
||||
modname := piece.ParentModule + "." + piece.Interfacing
|
||||
if interfaceModules[modname] == nil {
|
||||
interfaceModules[modname] = &module{
|
||||
ParentModule: piece.ParentModule,
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasSuffix(piece.GoFuncName, strings.ToLower("loader")) {
|
||||
shortDesc := piece.Doc[0]
|
||||
desc := piece.Doc[1:]
|
||||
interfaceModules[modname].ShortDescription = shortDesc
|
||||
interfaceModules[modname].Description = strings.Join(desc, "\n")
|
||||
interfaceModules[modname].Fields = piece.Fields
|
||||
interfaceModules[modname].Properties = piece.Properties
|
||||
continue
|
||||
}
|
||||
|
||||
interfaceModules[modname].Docs = append(interfaceModules[modname].Docs, piece)
|
||||
}
|
||||
|
||||
for _, piece := range typePieces {
|
||||
if !piece.IsInterface {
|
||||
filteredTypePieces = append(filteredTypePieces, piece)
|
||||
continue
|
||||
}
|
||||
|
||||
modname := piece.ParentModule + "." + piece.Interfacing
|
||||
if interfaceModules[modname] == nil {
|
||||
interfaceModules[modname] = &module{
|
||||
ParentModule: piece.ParentModule,
|
||||
}
|
||||
}
|
||||
|
||||
interfaceModules[modname].Types = append(interfaceModules[modname].Types, piece)
|
||||
}
|
||||
|
||||
fmt.Println(filteredTypePieces)
|
||||
if newDoc, ok := docs[mod]; ok {
|
||||
oldMod := docs[mod]
|
||||
newDoc.Types = append(filteredTypePieces, oldMod.Types...)
|
||||
newDoc.Docs = append(filteredPieces, oldMod.Docs...)
|
||||
|
||||
docs[mod] = newDoc
|
||||
} else {
|
||||
docs[mod] = module{
|
||||
Types: filteredTypePieces,
|
||||
Docs: filteredPieces,
|
||||
ShortDescription: shortDesc,
|
||||
Description: strings.Join(desc, "\n"),
|
||||
HasInterfaces: hasInterfaces,
|
||||
Properties: docPieceTag("property", tags),
|
||||
Fields: docPieceTag("field", tags),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, mod := range interfaceModules {
|
||||
docs[key] = *mod
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(docs) * 2)
|
||||
|
||||
for mod, v := range docs {
|
||||
docPath := "docs/api/" + mod + ".md"
|
||||
if v.HasInterfaces {
|
||||
os.Mkdir("docs/api/"+mod, 0777)
|
||||
os.Remove(docPath) // remove old doc path if it exists
|
||||
docPath = "docs/api/" + mod + "/_index.md"
|
||||
}
|
||||
if v.ParentModule != "" {
|
||||
docPath = "docs/api/" + v.ParentModule + "/" + mod + ".md"
|
||||
}
|
||||
|
||||
go func(modname, docPath string, modu module) {
|
||||
defer wg.Done()
|
||||
modOrIface := "Module"
|
||||
if modu.ParentModule != "" {
|
||||
modOrIface = "Module"
|
||||
}
|
||||
lastHeader := ""
|
||||
|
||||
f, _ := os.Create(docPath)
|
||||
f.WriteString(fmt.Sprintf(header, modOrIface, modname, modu.ShortDescription))
|
||||
typeTag, _ := regexp.Compile(`\B@\w+`)
|
||||
modDescription := typeTag.ReplaceAllStringFunc(strings.Replace(strings.Replace(modu.Description, "<", `\<`, -1), "{{\\<", "{{<", -1), func(typ string) string {
|
||||
typName := typ[1:]
|
||||
typLookup := typeTable[strings.ToLower(typName)]
|
||||
ifaces := typLookup[0] + "." + typLookup[1] + "/"
|
||||
if typLookup[1] == "" {
|
||||
ifaces = ""
|
||||
}
|
||||
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s#%s", typLookup[0], ifaces, strings.ToLower(typName))
|
||||
return fmt.Sprintf(`<a href="%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
|
||||
})
|
||||
f.WriteString(fmt.Sprintf("## Introduction\n%s\n\n", modDescription))
|
||||
if len(modu.Docs) != 0 {
|
||||
funcCount := 0
|
||||
for _, dps := range modu.Docs {
|
||||
if dps.IsMember {
|
||||
continue
|
||||
}
|
||||
funcCount++
|
||||
}
|
||||
|
||||
f.WriteString("## Functions\n")
|
||||
lastHeader = "functions"
|
||||
|
||||
mdTable := md.NewTable(funcCount, 2)
|
||||
mdTable.SetTitle(0, "")
|
||||
mdTable.SetTitle(1, "")
|
||||
|
||||
diff := 0
|
||||
for i, dps := range modu.Docs {
|
||||
if dps.IsMember {
|
||||
diff++
|
||||
continue
|
||||
}
|
||||
|
||||
mdTable.SetContent(i-diff, 0, fmt.Sprintf(`<a href="#%s">%s</a>`, dps.FuncName, dps.FuncSig))
|
||||
if len(dps.Doc) == 0 {
|
||||
fmt.Printf("WARNING! Function %s on module %s has no documentation!\n", dps.FuncName, modname)
|
||||
} else {
|
||||
mdTable.SetContent(i-diff, 1, dps.Doc[0])
|
||||
}
|
||||
}
|
||||
f.WriteString(mdTable.String())
|
||||
f.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(modu.Fields) != 0 {
|
||||
f.WriteString("## Static module fields\n")
|
||||
|
||||
mdTable := md.NewTable(len(modu.Fields), 2)
|
||||
mdTable.SetTitle(0, "")
|
||||
mdTable.SetTitle(1, "")
|
||||
|
||||
for i, dps := range modu.Fields {
|
||||
mdTable.SetContent(i, 0, dps.FuncName)
|
||||
mdTable.SetContent(i, 1, strings.Join(dps.Doc, " "))
|
||||
}
|
||||
f.WriteString(mdTable.String())
|
||||
f.WriteString("\n")
|
||||
}
|
||||
if len(modu.Properties) != 0 {
|
||||
f.WriteString("## Object properties\n")
|
||||
|
||||
mdTable := md.NewTable(len(modu.Fields), 2)
|
||||
mdTable.SetTitle(0, "")
|
||||
mdTable.SetTitle(1, "")
|
||||
|
||||
for i, dps := range modu.Properties {
|
||||
mdTable.SetContent(i, 0, dps.FuncName)
|
||||
mdTable.SetContent(i, 1, strings.Join(dps.Doc, " "))
|
||||
}
|
||||
f.WriteString(mdTable.String())
|
||||
f.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(modu.Docs) != 0 {
|
||||
if lastHeader != "functions" {
|
||||
f.WriteString("## Functions\n")
|
||||
}
|
||||
for _, dps := range modu.Docs {
|
||||
if dps.IsMember {
|
||||
continue
|
||||
}
|
||||
f.WriteString(fmt.Sprintf("<hr>\n<div id='%s'>", dps.FuncName))
|
||||
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(modname+"."+dps.FuncSig, "<", `\<`, -1), func(typ string) string {
|
||||
typName := typ[1:]
|
||||
typLookup := typeTable[strings.ToLower(typName)]
|
||||
ifaces := typLookup[0] + "." + typLookup[1] + "/"
|
||||
if typLookup[1] == "" {
|
||||
ifaces = ""
|
||||
}
|
||||
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s#%s", typLookup[0], ifaces, strings.ToLower(typName))
|
||||
return fmt.Sprintf(`<a href="%s" style="text-decoration: none;" id="lol">%s</a>`, linkedTyp, typName)
|
||||
})
|
||||
f.WriteString(fmt.Sprintf(`
|
||||
<h4 class='heading'>
|
||||
%s
|
||||
<a href="#%s" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
`, htmlSig, dps.FuncName))
|
||||
for _, doc := range dps.Doc {
|
||||
if !strings.HasPrefix(doc, "---") && doc != "" {
|
||||
f.WriteString(doc + " \n")
|
||||
}
|
||||
}
|
||||
f.WriteString("\n#### Parameters\n")
|
||||
if len(dps.Params) == 0 {
|
||||
f.WriteString("This function has no parameters. \n")
|
||||
}
|
||||
for _, p := range dps.Params {
|
||||
isVariadic := false
|
||||
typ := p.Type
|
||||
if strings.HasPrefix(p.Type, "...") {
|
||||
isVariadic = true
|
||||
typ = p.Type[3:]
|
||||
}
|
||||
|
||||
f.WriteString(fmt.Sprintf("`%s` **`%s`**", typ, p.Name))
|
||||
if isVariadic {
|
||||
f.WriteString(" (This type is variadic. You can pass an infinite amount of parameters with this type.)")
|
||||
}
|
||||
f.WriteString(" \n")
|
||||
f.WriteString(strings.Join(p.Doc, " "))
|
||||
f.WriteString("\n\n")
|
||||
}
|
||||
if codeExample := dps.Tags["example"]; codeExample != nil {
|
||||
f.WriteString("#### Example\n")
|
||||
f.WriteString(fmt.Sprintf("```lua\n%s\n```\n", strings.Join(codeExample[0].fields, "\n")))
|
||||
}
|
||||
f.WriteString("</div>")
|
||||
f.WriteString("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(modu.Types) != 0 {
|
||||
f.WriteString("## Types\n")
|
||||
for _, dps := range modu.Types {
|
||||
f.WriteString("<hr>\n\n")
|
||||
f.WriteString(fmt.Sprintf("## %s\n", dps.FuncName))
|
||||
for _, doc := range dps.Doc {
|
||||
if !strings.HasPrefix(doc, "---") {
|
||||
f.WriteString(doc + "\n")
|
||||
}
|
||||
}
|
||||
if len(dps.Properties) != 0 {
|
||||
f.WriteString("## Object properties\n")
|
||||
|
||||
mdTable := md.NewTable(len(dps.Properties), 2)
|
||||
mdTable.SetTitle(0, "")
|
||||
mdTable.SetTitle(1, "")
|
||||
|
||||
for i, d := range dps.Properties {
|
||||
mdTable.SetContent(i, 0, d.FuncName)
|
||||
mdTable.SetContent(i, 1, strings.Join(d.Doc, " "))
|
||||
}
|
||||
f.WriteString(mdTable.String())
|
||||
f.WriteString("\n")
|
||||
}
|
||||
f.WriteString("\n")
|
||||
f.WriteString("### Methods\n")
|
||||
for _, dps := range modu.Docs {
|
||||
if !dps.IsMember {
|
||||
continue
|
||||
}
|
||||
htmlSig := typeTag.ReplaceAllStringFunc(strings.Replace(dps.FuncSig, "<", `\<`, -1), func(typ string) string {
|
||||
typName := regexp.MustCompile(`\w+`).FindString(typ[1:])
|
||||
typLookup := typeTable[strings.ToLower(typName)]
|
||||
fmt.Printf("%+q, \n", typLookup)
|
||||
linkedTyp := fmt.Sprintf("/Hilbish/docs/api/%s/%s/#%s", typLookup[0], typLookup[0]+"."+typLookup[1], strings.ToLower(typName))
|
||||
return fmt.Sprintf(`<a href="#%s" style="text-decoration: none;">%s</a>`, linkedTyp, typName)
|
||||
})
|
||||
f.WriteString(fmt.Sprintf("#### %s\n", htmlSig))
|
||||
for _, doc := range dps.Doc {
|
||||
if !strings.HasPrefix(doc, "---") {
|
||||
f.WriteString(doc + "\n")
|
||||
}
|
||||
}
|
||||
f.WriteString("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}(mod, docPath, v)
|
||||
|
||||
go func(md, modname string, modu module) {
|
||||
defer wg.Done()
|
||||
|
||||
if modu.ParentModule != "" {
|
||||
return
|
||||
}
|
||||
|
||||
ff, _ := os.Create("emmyLuaDocs/" + modname + ".lua")
|
||||
ff.WriteString("--- @meta\n\nlocal " + modname + " = {}\n\n")
|
||||
for _, em := range emmyDocs[modname] {
|
||||
if strings.HasSuffix(em.DocPiece.GoFuncName, strings.ToLower("loader")) {
|
||||
continue
|
||||
}
|
||||
|
||||
dps := em.DocPiece
|
||||
funcdocs := dps.Doc
|
||||
ff.WriteString("--- " + strings.Join(funcdocs, "\n--- ") + "\n")
|
||||
if len(em.Annotations) != 0 {
|
||||
ff.WriteString(strings.Join(em.Annotations, "\n") + "\n")
|
||||
}
|
||||
accessor := "."
|
||||
if dps.IsMember {
|
||||
accessor = ":"
|
||||
}
|
||||
signature := strings.Split(dps.FuncSig, " ->")[0]
|
||||
var intrface string
|
||||
if dps.IsInterface {
|
||||
intrface = "." + dps.Interfacing
|
||||
}
|
||||
ff.WriteString("function " + modname + intrface + accessor + signature + " end\n\n")
|
||||
}
|
||||
ff.WriteString("return " + modname + "\n")
|
||||
}(mod, mod, v)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
227
cmd/docgen/docgen.lua
Normal file
@ -0,0 +1,227 @@
|
||||
local fs = require 'fs'
|
||||
local emmyPattern = '^%-%-%- (.+)'
|
||||
local emmyPattern2 = '^%-%- (.+)'
|
||||
local modpattern = '^%-+ @module (.+)'
|
||||
local pieces = {}
|
||||
local descriptions = {}
|
||||
|
||||
local files = fs.readdir 'nature'
|
||||
for _, fname in ipairs(files) do
|
||||
local isScript = fname:match'%.lua$'
|
||||
if not isScript then goto continue end
|
||||
|
||||
local f = io.open(string.format('nature/%s', fname))
|
||||
local header = f:read '*l'
|
||||
local mod = header:match(modpattern)
|
||||
if not mod then goto continue end
|
||||
|
||||
pieces[mod] = {}
|
||||
descriptions[mod] = {}
|
||||
|
||||
local docPiece = {}
|
||||
local lines = {}
|
||||
local lineno = 0
|
||||
local doingDescription = true
|
||||
|
||||
for line in f:lines() do
|
||||
lineno = lineno + 1
|
||||
lines[lineno] = line
|
||||
|
||||
if line == header then goto continue2 end
|
||||
if line:match(emmyPattern) or line:match(emmyPattern2) then
|
||||
if doingDescription then
|
||||
table.insert(descriptions[mod], line:match(emmyPattern) or line:match(emmyPattern2))
|
||||
end
|
||||
else
|
||||
doingDescription = false
|
||||
if line:match '^function' then
|
||||
local pattern = (string.format('^function %s%%.', mod) .. '(%w+)')
|
||||
local funcName = line:match(pattern)
|
||||
if not funcName then goto continue2 end
|
||||
|
||||
local dps = {
|
||||
description = {},
|
||||
example = {},
|
||||
params = {}
|
||||
}
|
||||
|
||||
local offset = 1
|
||||
local doingExample = false
|
||||
while true do
|
||||
local prev = lines[lineno - offset]
|
||||
|
||||
local docline = prev:match '^%-+ (.+)'
|
||||
if docline then
|
||||
local emmy = docline:match '@(%w+)'
|
||||
local cut = 0
|
||||
|
||||
if emmy then cut = emmy:len() + 3 end
|
||||
local emmythings = string.split(docline:sub(cut), ' ')
|
||||
|
||||
if emmy then
|
||||
if emmy == 'param' then
|
||||
table.insert(dps.params, 1, {
|
||||
name = emmythings[1],
|
||||
type = emmythings[2],
|
||||
-- the +1 accounts for space.
|
||||
description = table.concat(emmythings, ' '):sub(emmythings[1]:len() + 1 + emmythings[2]:len() + 1)
|
||||
})
|
||||
end
|
||||
else
|
||||
if docline:match '#example' then
|
||||
doingExample = not doingExample
|
||||
end
|
||||
|
||||
if not docline:match '#example' then
|
||||
if doingExample then
|
||||
table.insert(dps.example, 1, docline)
|
||||
else
|
||||
table.insert(dps.description, 1, docline)
|
||||
end
|
||||
end
|
||||
end
|
||||
offset = offset + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(pieces[mod], {funcName, dps})
|
||||
end
|
||||
docPiece = {}
|
||||
goto continue2
|
||||
end
|
||||
|
||||
table.insert(docPiece, line)
|
||||
::continue2::
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
local header = [[---
|
||||
title: %s %s
|
||||
description: %s
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "%s"
|
||||
---
|
||||
|
||||
]]
|
||||
|
||||
for iface, dps in pairs(pieces) do
|
||||
local mod = iface:match '(%w+)%.' or 'nature'
|
||||
local docParent = 'Nature'
|
||||
|
||||
path = string.format('docs/%s/%s.md', mod, iface)
|
||||
if mod ~= 'nature' then
|
||||
docParent = "API"
|
||||
path = string.format('docs/api/%s/%s.md', mod, iface)
|
||||
end
|
||||
if iface == 'hilbish' then
|
||||
docParent = "API"
|
||||
path = string.format('docs/api/hilbish/_index.md', mod, iface)
|
||||
end
|
||||
|
||||
fs.mkdir(fs.dir(path), true)
|
||||
|
||||
local exists = pcall(fs.stat, path)
|
||||
local newOrNotNature = (exists and mod ~= 'nature') or iface == 'hilbish'
|
||||
|
||||
local f <close> = io.open(path, newOrNotNature and 'r+' or 'w+')
|
||||
local tocPos
|
||||
if not newOrNotNature then
|
||||
f:write(string.format(header, 'Module', iface, (descriptions[iface] and #descriptions[iface] > 0) and descriptions[iface][1] or 'No description.', docParent))
|
||||
if descriptions[iface] and #descriptions[iface] > 0 then
|
||||
table.remove(descriptions[iface], 1)
|
||||
f:write(string.format('\n## Introduction\n%s\n\n', table.concat(descriptions[iface], '\n')))
|
||||
f:write('## Functions\n')
|
||||
f:write([[|||
|
||||
|----|----|
|
||||
]])
|
||||
tocPos = f:seek()
|
||||
end
|
||||
end
|
||||
|
||||
local tocSearch = false
|
||||
for line in f:lines() do
|
||||
if line:match '^## Functions' then
|
||||
tocSearch = true
|
||||
end
|
||||
if tocSearch and line == '' then
|
||||
tocSearch = false
|
||||
tocPos = f:seek() - 1
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(dps, function(a, b) return a[1] < b[1] end)
|
||||
for _, piece in pairs(dps) do
|
||||
local func = piece[1]
|
||||
local docs = piece[2]
|
||||
local sig = string.format('%s.%s(', iface, func)
|
||||
local params = ''
|
||||
for idx, param in ipairs(docs.params) do
|
||||
sig = sig .. param.name:gsub('%?$', '')
|
||||
params = params .. param.name:gsub('%?$', '')
|
||||
if idx ~= #docs.params then
|
||||
sig = sig .. ', '
|
||||
params = params .. ', '
|
||||
end
|
||||
end
|
||||
sig = sig .. ')'
|
||||
|
||||
if tocPos then
|
||||
f:seek('set', tocPos)
|
||||
local contents = f:read '*a'
|
||||
f:seek('set', tocPos)
|
||||
local tocLine = string.format('|<a href="#%s">%s</a>|%s|\n', func, string.format('%s(%s)', func, params), docs.description[1])
|
||||
f:write(tocLine .. contents)
|
||||
f:seek 'end'
|
||||
end
|
||||
|
||||
f:write(string.format('<hr>\n<div id=\'%s\'>\n', func))
|
||||
f:write(string.format([[
|
||||
<h4 class='heading'>
|
||||
%s
|
||||
<a href="#%s" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
]], sig, func))
|
||||
|
||||
f:write(table.concat(docs.description, '\n') .. '\n')
|
||||
f:write '#### Parameters\n'
|
||||
if #docs.params == 0 then
|
||||
f:write 'This function has no parameters. \n'
|
||||
end
|
||||
for _, param in ipairs(docs.params) do
|
||||
f:write(string.format('`%s` **`%s`** \n', param.name:gsub('%?$', ''), param.type))
|
||||
f:write(string.format('%s\n\n', param.description))
|
||||
end
|
||||
if #docs.example ~= 0 then
|
||||
f:write '#### Example\n'
|
||||
f:write(string.format('```lua\n%s\n```\n', table.concat(docs.example, '\n')))
|
||||
end
|
||||
--[[
|
||||
local params = table.filter(docs, function(t)
|
||||
return t:match '^%-%-%- @param'
|
||||
end)
|
||||
for i, str in ipairs(params) do
|
||||
if i ~= 1 then
|
||||
f:write ', '
|
||||
end
|
||||
f:write(str:match '^%-%-%- @param ([%w]+) ')
|
||||
end
|
||||
f:write(')\n')
|
||||
|
||||
for _, str in ipairs(docs) do
|
||||
if not str:match '^%-%-%- @' then
|
||||
f:write(str:match '^%-%-%- (.+)' .. '\n')
|
||||
end
|
||||
end
|
||||
]]--
|
||||
f:write('</div>')
|
||||
f:write('\n\n')
|
||||
end
|
||||
end
|
||||
376
complete.go
Normal file
@ -0,0 +1,376 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"os"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
var charEscapeMap = []string{
|
||||
"\"", "\\\"",
|
||||
"'", "\\'",
|
||||
"`", "\\`",
|
||||
" ", "\\ ",
|
||||
"(", "\\(",
|
||||
")", "\\)",
|
||||
"[", "\\[",
|
||||
"]", "\\]",
|
||||
"$", "\\$",
|
||||
"&", "\\&",
|
||||
"*", "\\*",
|
||||
">", "\\>",
|
||||
"<", "\\<",
|
||||
"|", "\\|",
|
||||
}
|
||||
var charEscapeMapInvert = invert(charEscapeMap)
|
||||
var escapeReplaer = strings.NewReplacer(charEscapeMap...)
|
||||
var escapeInvertReplaer = strings.NewReplacer(charEscapeMapInvert...)
|
||||
|
||||
func invert(m []string) []string {
|
||||
newM := make([]string, len(charEscapeMap))
|
||||
for i := range m {
|
||||
if (i + 1) % 2 == 0 {
|
||||
newM[i] = m[i - 1]
|
||||
newM[i - 1] = m[i]
|
||||
}
|
||||
}
|
||||
|
||||
return newM
|
||||
}
|
||||
|
||||
func splitForFile(str string) []string {
|
||||
split := []string{}
|
||||
sb := &strings.Builder{}
|
||||
quoted := false
|
||||
|
||||
for i, r := range str {
|
||||
if r == '"' {
|
||||
quoted = !quoted
|
||||
sb.WriteRune(r)
|
||||
} else if r == ' ' && str[i - 1] == '\\' {
|
||||
sb.WriteRune(r)
|
||||
} else if !quoted && r == ' ' {
|
||||
split = append(split, sb.String())
|
||||
sb.Reset()
|
||||
} else {
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
}
|
||||
if strings.HasSuffix(str, " ") {
|
||||
split = append(split, "")
|
||||
}
|
||||
|
||||
if sb.Len() > 0 {
|
||||
split = append(split, sb.String())
|
||||
}
|
||||
|
||||
return split
|
||||
}
|
||||
|
||||
func fileComplete(query, ctx string, fields []string) ([]string, string) {
|
||||
q := splitForFile(ctx)
|
||||
path := ""
|
||||
if len(q) != 0 {
|
||||
path = q[len(q) - 1]
|
||||
}
|
||||
|
||||
return matchPath(path)
|
||||
}
|
||||
|
||||
func binaryComplete(query, ctx string, fields []string) ([]string, string) {
|
||||
q := splitForFile(ctx)
|
||||
query = ""
|
||||
if len(q) != 0 {
|
||||
query = q[len(q) - 1]
|
||||
}
|
||||
|
||||
var completions []string
|
||||
|
||||
prefixes := []string{"./", "../", "/", "~/"}
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(query, prefix) {
|
||||
fileCompletions, filePref := matchPath(query)
|
||||
if len(fileCompletions) != 0 {
|
||||
for _, f := range fileCompletions {
|
||||
fullPath, _ := filepath.Abs(util.ExpandHome(query + strings.TrimPrefix(f, filePref)))
|
||||
if err := util.FindExecutable(escapeInvertReplaer.Replace(fullPath), false, true); err != nil {
|
||||
continue
|
||||
}
|
||||
completions = append(completions, f)
|
||||
}
|
||||
}
|
||||
return completions, filePref
|
||||
}
|
||||
}
|
||||
|
||||
// filter out executables, but in path
|
||||
for _, dir := range filepath.SplitList(os.Getenv("PATH")) {
|
||||
// search for an executable which matches our query string
|
||||
if matches, err := filepath.Glob(filepath.Join(dir, query + "*")); err == nil {
|
||||
// get basename from matches
|
||||
for _, match := range matches {
|
||||
// check if we have execute permissions for our match
|
||||
err := util.FindExecutable(match, true, false)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// get basename from match
|
||||
name := filepath.Base(match)
|
||||
// add basename to completions
|
||||
completions = append(completions, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add lua registered commands to completions
|
||||
for cmdName := range cmds.Commands {
|
||||
if strings.HasPrefix(cmdName, query) {
|
||||
completions = append(completions, cmdName)
|
||||
}
|
||||
}
|
||||
|
||||
completions = removeDupes(completions)
|
||||
|
||||
return completions, query
|
||||
}
|
||||
|
||||
func matchPath(query string) ([]string, string) {
|
||||
oldQuery := query
|
||||
query = strings.TrimPrefix(query, "\"")
|
||||
var entries []string
|
||||
var baseName string
|
||||
|
||||
query = escapeInvertReplaer.Replace(query)
|
||||
path, _ := filepath.Abs(util.ExpandHome(filepath.Dir(query)))
|
||||
if string(query) == "" {
|
||||
// filepath base below would give us "."
|
||||
// which would cause a match of only dotfiles
|
||||
path, _ = filepath.Abs(".")
|
||||
} else if !strings.HasSuffix(query, string(os.PathSeparator)) {
|
||||
baseName = filepath.Base(query)
|
||||
}
|
||||
|
||||
files, _ := os.ReadDir(path)
|
||||
for _, entry := range files {
|
||||
file, err := entry.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if file.Mode() & os.ModeSymlink != 0 {
|
||||
path, err := filepath.EvalSymlinks(filepath.Join(path, file.Name()))
|
||||
if err == nil {
|
||||
file, err = os.Lstat(path)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(file.Name(), baseName) {
|
||||
entry := file.Name()
|
||||
if file.IsDir() {
|
||||
entry = entry + string(os.PathSeparator)
|
||||
}
|
||||
if !strings.HasPrefix(oldQuery, "\"") {
|
||||
entry = escapeFilename(entry)
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
}
|
||||
if !strings.HasPrefix(oldQuery, "\"") {
|
||||
baseName = escapeFilename(baseName)
|
||||
}
|
||||
|
||||
return entries, baseName
|
||||
}
|
||||
|
||||
func escapeFilename(fname string) string {
|
||||
return escapeReplaer.Replace(fname)
|
||||
}
|
||||
|
||||
// #interface completion
|
||||
// tab completions
|
||||
// The completions interface deals with tab completions.
|
||||
func completionLoader(rtm *rt.Runtime) *rt.Table {
|
||||
exports := map[string]util.LuaExport{
|
||||
"bins": {hcmpBins, 3, false},
|
||||
"call": {hcmpCall, 4, false},
|
||||
"files": {hcmpFiles, 3, false},
|
||||
"handler": {hcmpHandler, 2, false},
|
||||
}
|
||||
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, exports)
|
||||
|
||||
return mod
|
||||
}
|
||||
|
||||
// #interface completion
|
||||
// bins(query, ctx, fields) -> entries (table), prefix (string)
|
||||
// Return binaries/executables based on the provided parameters.
|
||||
// This function is meant to be used as a helper in a command completion handler.
|
||||
// #param query string
|
||||
// #param ctx string
|
||||
// #param fields table
|
||||
/*
|
||||
#example
|
||||
-- an extremely simple completer for sudo.
|
||||
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||
table.remove(fields, 1)
|
||||
if #fields[1] then
|
||||
-- return commands because sudo runs a command as root..!
|
||||
|
||||
local entries, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||
return {
|
||||
type = 'grid',
|
||||
items = entries
|
||||
}, pfx
|
||||
end
|
||||
|
||||
-- ... else suggest files or anything else ..
|
||||
end)
|
||||
#example
|
||||
*/
|
||||
func hcmpBins(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
query, ctx, fds, err := getCompleteParams(t, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
completions, pfx := binaryComplete(query, ctx, fds)
|
||||
luaComps := rt.NewTable()
|
||||
|
||||
for i, comp := range completions {
|
||||
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
||||
}
|
||||
|
||||
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
||||
}
|
||||
|
||||
// #interface completion
|
||||
// call(name, query, ctx, fields) -> completionGroups (table), prefix (string)
|
||||
// Calls a completer function. This is mainly used to call a command completer, which will have a `name`
|
||||
// in the form of `command.name`, example: `command.git`.
|
||||
// You can check the Completions doc or `doc completions` for info on the `completionGroups` return value.
|
||||
// #param name string
|
||||
// #param query string
|
||||
// #param ctx string
|
||||
// #param fields table
|
||||
func hcmpCall(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(4); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
completer, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query, err := c.StringArg(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, err := c.StringArg(2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields, err := c.TableArg(3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var completecb *rt.Closure
|
||||
var ok bool
|
||||
if completecb, ok = luaCompletions[completer]; !ok {
|
||||
return nil, errors.New("completer " + completer + " does not exist")
|
||||
}
|
||||
|
||||
// we must keep the holy 80 cols
|
||||
cont := c.Next()
|
||||
err = rt.Call(l.MainThread(), rt.FunctionValue(completecb),
|
||||
[]rt.Value{rt.StringValue(query), rt.StringValue(ctx), rt.TableValue(fields)},
|
||||
cont)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cont, nil
|
||||
}
|
||||
|
||||
// #interface completion
|
||||
// files(query, ctx, fields) -> entries (table), prefix (string)
|
||||
// Returns file matches based on the provided parameters.
|
||||
// This function is meant to be used as a helper in a command completion handler.
|
||||
// #param query string
|
||||
// #param ctx string
|
||||
// #param fields table
|
||||
func hcmpFiles(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
query, ctx, fds, err := getCompleteParams(t, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
completions, pfx := fileComplete(query, ctx, fds)
|
||||
luaComps := rt.NewTable()
|
||||
|
||||
for i, comp := range completions {
|
||||
luaComps.Set(rt.IntValue(int64(i + 1)), rt.StringValue(comp))
|
||||
}
|
||||
|
||||
return c.PushingNext(t.Runtime, rt.TableValue(luaComps), rt.StringValue(pfx)), nil
|
||||
}
|
||||
|
||||
// #interface completion
|
||||
// handler(line, pos)
|
||||
// This function contains the general completion handler for Hilbish. This function handles
|
||||
// completion of everything, which includes calling other command handlers, binaries, and files.
|
||||
// This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function.
|
||||
// #param line string The current Hilbish command line
|
||||
// #param pos number Numerical position of the cursor
|
||||
/*
|
||||
#example
|
||||
-- stripped down version of the default implementation
|
||||
function hilbish.completion.handler(line, pos)
|
||||
local query = fields[#fields]
|
||||
|
||||
if #fields == 1 then
|
||||
-- call bins handler here
|
||||
else
|
||||
-- call command completer or files completer here
|
||||
end
|
||||
end
|
||||
#example
|
||||
*/
|
||||
func hcmpHandler(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
|
||||
func getCompleteParams(t *rt.Thread, c *rt.GoCont) (string, string, []string, error) {
|
||||
if err := c.CheckNArgs(3); err != nil {
|
||||
return "", "", []string{}, err
|
||||
}
|
||||
query, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return "", "", []string{}, err
|
||||
}
|
||||
ctx, err := c.StringArg(1)
|
||||
if err != nil {
|
||||
return "", "", []string{}, err
|
||||
}
|
||||
fields, err := c.TableArg(2)
|
||||
if err != nil {
|
||||
return "", "", []string{}, err
|
||||
}
|
||||
|
||||
var fds []string
|
||||
util.ForEach(fields, func(k rt.Value, v rt.Value) {
|
||||
if v.Type() == rt.StringType {
|
||||
fds = append(fds, v.AsString())
|
||||
}
|
||||
})
|
||||
|
||||
return query, ctx, fds, err
|
||||
}
|
||||
13
docs/_index.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
title: Introduction
|
||||
layout: doc
|
||||
weight: -1
|
||||
menu: docs
|
||||
---
|
||||
|
||||
Hilbish is a hyper-extensible shell mainly intended for interactive use.
|
||||
To enhance the interactive experience, Hilbish comes with a wide range
|
||||
of features and sane defaults, including a nice looking prompt,
|
||||
advanced completion menus and history search.
|
||||
|
||||
Here documents some of the features of Hilbish and the Lua API.
|
||||
9
docs/api/_index.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
title: API
|
||||
layout: doc
|
||||
weight: -100
|
||||
menu: docs
|
||||
---
|
||||
|
||||
Welcome to the API documentation for Hilbish. This documents Lua functions
|
||||
provided by Hilbish.
|
||||
167
docs/api/bait.md
Normal file
@ -0,0 +1,167 @@
|
||||
---
|
||||
title: Module bait
|
||||
description: the event emitter
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Bait is the event emitter for Hilbish. Much like Node.js and
|
||||
its `events` system, many actions in Hilbish emit events.
|
||||
Unlike Node.js, Hilbish events are global. So make sure to
|
||||
pick a unique name!
|
||||
|
||||
Usage of the Bait module consists of userstanding
|
||||
event-driven architecture, but it's pretty simple:
|
||||
If you want to act on a certain event, you can `catch` it.
|
||||
You can act on events via callback functions.
|
||||
|
||||
Examples of this are in the Hilbish default config!
|
||||
Consider this part of it:
|
||||
```lua
|
||||
bait.catch('command.exit', function(code)
|
||||
running = false
|
||||
doPrompt(code ~= 0)
|
||||
doNotifyPrompt()
|
||||
end)
|
||||
```
|
||||
|
||||
What this does is, whenever the `command.exit` event is thrown,
|
||||
this function will set the user prompt.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#catch">catch(name, cb)</a>|Catches an event. This function can be used to act on events.|
|
||||
|<a href="#catchOnce">catchOnce(name, cb)</a>|Catches an event, but only once. This will remove the hook immediately after it runs for the first time.|
|
||||
|<a href="#hooks">hooks(name) -> table</a>|Returns a table of functions that are hooked on an event with the corresponding `name`.|
|
||||
|<a href="#release">release(name, catcher)</a>|Removes the `catcher` for the event with `name`.|
|
||||
|<a href="#throw">throw(name, ...args)</a>|Throws a hook with `name` with the provided `args`.|
|
||||
|
||||
<hr>
|
||||
<div id='catch'>
|
||||
<h4 class='heading'>
|
||||
bait.catch(name, cb)
|
||||
<a href="#catch" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Catches an event. This function can be used to act on events.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
The name of the hook.
|
||||
|
||||
`function` **`cb`**
|
||||
The function that will be called when the hook is thrown.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
bait.catch('hilbish.exit', function()
|
||||
print 'Goodbye Hilbish!'
|
||||
end)
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='catchOnce'>
|
||||
<h4 class='heading'>
|
||||
bait.catchOnce(name, cb)
|
||||
<a href="#catchOnce" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Catches an event, but only once. This will remove the hook immediately after it runs for the first time.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
The name of the event
|
||||
|
||||
`function` **`cb`**
|
||||
The function that will be called when the event is thrown.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='hooks'>
|
||||
<h4 class='heading'>
|
||||
bait.hooks(name) -> table
|
||||
<a href="#hooks" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns a table of functions that are hooked on an event with the corresponding `name`.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
The name of the hook
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='release'>
|
||||
<h4 class='heading'>
|
||||
bait.release(name, catcher)
|
||||
<a href="#release" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Removes the `catcher` for the event with `name`.
|
||||
For this to work, `catcher` has to be the same function used to catch
|
||||
an event, like one saved to a variable.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
Name of the event the hook is on
|
||||
|
||||
`function` **`catcher`**
|
||||
Hook function to remove
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
local hookCallback = function() print 'hi' end
|
||||
|
||||
bait.catch('event', hookCallback)
|
||||
|
||||
-- a little while later....
|
||||
bait.release('event', hookCallback)
|
||||
-- and now hookCallback will no longer be ran for the event.
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='throw'>
|
||||
<h4 class='heading'>
|
||||
bait.throw(name, ...args)
|
||||
<a href="#throw" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Throws a hook with `name` with the provided `args`.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
The name of the hook.
|
||||
|
||||
`any` **`args`** (This type is variadic. You can pass an infinite amount of parameters with this type.)
|
||||
The arguments to pass to the hook.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
bait.throw('greeting', 'world')
|
||||
|
||||
-- This can then be listened to via
|
||||
bait.catch('gretting', function(greetTo)
|
||||
print('Hello ' .. greetTo)
|
||||
end)
|
||||
```
|
||||
</div>
|
||||
|
||||
113
docs/api/commander.md
Normal file
@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Module commander
|
||||
description: library for custom commands
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Commander is the library which handles Hilbish commands. This makes
|
||||
the user able to add Lua-written commands to their shell without making
|
||||
a separate script in a bin folder. Instead, you may simply use the Commander
|
||||
library in your Hilbish config.
|
||||
|
||||
```lua
|
||||
local commander = require 'commander'
|
||||
|
||||
commander.register('hello', function(args, sinks)
|
||||
sinks.out:writeln 'Hello world!'
|
||||
end)
|
||||
```
|
||||
|
||||
In this example, a command with the name of `hello` is created
|
||||
that will print `Hello world!` to output. One question you may
|
||||
have is: What is the `sinks` parameter?
|
||||
|
||||
The `sinks` parameter is a table with 3 keys: `input`, `out`, and `err`.
|
||||
There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`)
|
||||
as `in` is also a Lua keyword, so `input` is preferred for use.
|
||||
All of them are a <a href="/Hilbish/docs/api/hilbish/#sink" style="text-decoration: none;">Sink</a>.
|
||||
In the future, `sinks.in` will be removed.
|
||||
|
||||
- `in` is the standard input.
|
||||
You may use the read functions on this sink to get input from the user.
|
||||
- `out` is standard output.
|
||||
This is usually where command output should go.
|
||||
- `err` is standard error.
|
||||
This sink is for writing errors, as the name would suggest.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#deregister">deregister(name)</a>|Removes the named command. Note that this will only remove Commander-registered commands.|
|
||||
|<a href="#register">register(name, cb)</a>|Adds a new command with the given `name`. When Hilbish has to run a command with a name,|
|
||||
|<a href="#registry">registry() -> table</a>|Returns all registered commanders. Returns a list of tables with the following keys:|
|
||||
|
||||
<hr>
|
||||
<div id='deregister'>
|
||||
<h4 class='heading'>
|
||||
commander.deregister(name)
|
||||
<a href="#deregister" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Removes the named command. Note that this will only remove Commander-registered commands.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
Name of the command to remove.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='register'>
|
||||
<h4 class='heading'>
|
||||
commander.register(name, cb)
|
||||
<a href="#register" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Adds a new command with the given `name`. When Hilbish has to run a command with a name,
|
||||
it will run the function providing the arguments and sinks.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
Name of the command
|
||||
|
||||
`function` **`cb`**
|
||||
Callback to handle command invocation
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- When you run the command `hello` in the shell, it will print `Hello world`.
|
||||
-- If you run it with, for example, `hello Hilbish`, it will print 'Hello Hilbish'
|
||||
commander.register('hello', function(args, sinks)
|
||||
local name = 'world'
|
||||
if #args > 0 then name = args[1] end
|
||||
|
||||
sinks.out:writeln('Hello ' .. name)
|
||||
end)
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='registry'>
|
||||
<h4 class='heading'>
|
||||
commander.registry() -> table
|
||||
<a href="#registry" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns all registered commanders. Returns a list of tables with the following keys:
|
||||
- `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
257
docs/api/fs.md
Normal file
@ -0,0 +1,257 @@
|
||||
---
|
||||
title: Module fs
|
||||
description: filesystem interaction and functionality library
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The fs module provides filesystem functions to Hilbish. While Lua's standard
|
||||
library has some I/O functions, they're missing a lot of the basics. The `fs`
|
||||
library offers more functions and will work on any operating system Hilbish does.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#abs">abs(path) -> string</a>|Returns an absolute version of the `path`.|
|
||||
|<a href="#basename">basename(path) -> string</a>|Returns the "basename," or the last part of the provided `path`. If path is empty,|
|
||||
|<a href="#cd">cd(dir)</a>|Changes Hilbish's directory to `dir`.|
|
||||
|<a href="#dir">dir(path) -> string</a>|Returns the directory part of `path`. If a file path like|
|
||||
|<a href="#glob">glob(pattern) -> matches (table)</a>|Match all files based on the provided `pattern`.|
|
||||
|<a href="#join">join(...path) -> string</a>|Takes any list of paths and joins them based on the operating system's path separator.|
|
||||
|<a href="#mkdir">mkdir(name, recursive)</a>|Creates a new directory with the provided `name`.|
|
||||
|<a href="#pipe">fpipe() -> File, File</a>|Returns a pair of connected files, also known as a pipe.|
|
||||
|<a href="#readdir">readdir(path) -> table[string]</a>|Returns a list of all files and directories in the provided path.|
|
||||
|<a href="#stat">stat(path) -> {}</a>|Returns the information about a given `path`.|
|
||||
|
||||
## Static module fields
|
||||
|||
|
||||
|----|----|
|
||||
|pathSep|The operating system's path separator.|
|
||||
|
||||
<hr>
|
||||
<div id='abs'>
|
||||
<h4 class='heading'>
|
||||
fs.abs(path) -> string
|
||||
<a href="#abs" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns an absolute version of the `path`.
|
||||
This can be used to resolve short paths like `..` to `/home/user`.
|
||||
|
||||
#### Parameters
|
||||
`string` **`path`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='basename'>
|
||||
<h4 class='heading'>
|
||||
fs.basename(path) -> string
|
||||
<a href="#basename" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the "basename," or the last part of the provided `path`. If path is empty,
|
||||
`.` will be returned.
|
||||
|
||||
#### Parameters
|
||||
`string` **`path`**
|
||||
Path to get the base name of.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='cd'>
|
||||
<h4 class='heading'>
|
||||
fs.cd(dir)
|
||||
<a href="#cd" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Changes Hilbish's directory to `dir`.
|
||||
|
||||
#### Parameters
|
||||
`string` **`dir`**
|
||||
Path to change directory to.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='dir'>
|
||||
<h4 class='heading'>
|
||||
fs.dir(path) -> string
|
||||
<a href="#dir" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the directory part of `path`. If a file path like
|
||||
`~/Documents/doc.txt` then this function will return `~/Documents`.
|
||||
|
||||
#### Parameters
|
||||
`string` **`path`**
|
||||
Path to get the directory for.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='glob'>
|
||||
<h4 class='heading'>
|
||||
fs.glob(pattern) -> matches (table)
|
||||
<a href="#glob" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Match all files based on the provided `pattern`.
|
||||
For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match
|
||||
|
||||
#### Parameters
|
||||
`string` **`pattern`**
|
||||
Pattern to compare files with.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
--[[
|
||||
Within a folder that contains the following files:
|
||||
a.txt
|
||||
init.lua
|
||||
code.lua
|
||||
doc.pdf
|
||||
]]--
|
||||
local matches = fs.glob './*.lua'
|
||||
print(matches)
|
||||
-- -> {'init.lua', 'code.lua'}
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='join'>
|
||||
<h4 class='heading'>
|
||||
fs.join(...path) -> string
|
||||
<a href="#join" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Takes any list of paths and joins them based on the operating system's path separator.
|
||||
|
||||
#### Parameters
|
||||
`string` **`path`** (This type is variadic. You can pass an infinite amount of parameters with this type.)
|
||||
Paths to join together
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- This prints the directory for Hilbish's config!
|
||||
print(fs.join(hilbish.userDir.config, 'hilbish'))
|
||||
-- -> '/home/user/.config/hilbish' on Linux
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='mkdir'>
|
||||
<h4 class='heading'>
|
||||
fs.mkdir(name, recursive)
|
||||
<a href="#mkdir" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Creates a new directory with the provided `name`.
|
||||
With `recursive`, mkdir will create parent directories.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
Name of the directory
|
||||
|
||||
`boolean` **`recursive`**
|
||||
Whether to create parent directories for the provided name
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- This will create the directory foo, then create the directory bar in the
|
||||
-- foo directory. If recursive is false in this case, it will fail.
|
||||
fs.mkdir('./foo/bar', true)
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='pipe'>
|
||||
<h4 class='heading'>
|
||||
fs.fpipe() -> File, File
|
||||
<a href="#pipe" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns a pair of connected files, also known as a pipe.
|
||||
The type returned is a Lua file, same as returned from `io` functions.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='readdir'>
|
||||
<h4 class='heading'>
|
||||
fs.readdir(path) -> table[string]
|
||||
<a href="#readdir" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns a list of all files and directories in the provided path.
|
||||
|
||||
#### Parameters
|
||||
`string` **`dir`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='stat'>
|
||||
<h4 class='heading'>
|
||||
fs.stat(path) -> {}
|
||||
<a href="#stat" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the information about a given `path`.
|
||||
The returned table contains the following values:
|
||||
name (string) - Name of the path
|
||||
size (number) - Size of the path in bytes
|
||||
mode (string) - Unix permission mode in an octal format string (with leading 0)
|
||||
isDir (boolean) - If the path is a directory
|
||||
|
||||
#### Parameters
|
||||
`string` **`path`**
|
||||
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
local inspect = require 'inspect'
|
||||
|
||||
local stat = fs.stat '~'
|
||||
print(inspect(stat))
|
||||
--[[
|
||||
Would print the following:
|
||||
{
|
||||
isDir = true,
|
||||
mode = "0755",
|
||||
name = "username",
|
||||
size = 12288
|
||||
}
|
||||
]]--
|
||||
```
|
||||
</div>
|
||||
|
||||
541
docs/api/hilbish/_index.md
Normal file
@ -0,0 +1,541 @@
|
||||
---
|
||||
title: Module hilbish
|
||||
description: the core Hilbish API
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
The Hilbish module includes the core API, containing
|
||||
interfaces and functions which directly relate to shell functionality.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#alias">alias(cmd, orig)</a>|Sets an alias, with a name of `cmd` to another command.|
|
||||
|<a href="#appendPath">appendPath(dir)</a>|Appends the provided dir to the command path (`$PATH`)|
|
||||
|<a href="#complete">complete(scope, cb)</a>|Registers a completion handler for the specified scope.|
|
||||
|<a href="#cwd">cwd() -> string</a>|Returns the current directory of the shell.|
|
||||
|<a href="#exec">exec(cmd)</a>|Replaces the currently running Hilbish instance with the supplied command.|
|
||||
|<a href="#goro">goro(fn)</a>|Puts `fn` in a Goroutine.|
|
||||
|<a href="#highlighter">highlighter(line)</a>|Line highlighter handler.|
|
||||
|<a href="#hinter">hinter(line, pos)</a>|The command line hint handler. It gets called on every key insert to|
|
||||
|<a href="#inputMode">inputMode(mode)</a>|Sets the input mode for Hilbish's line reader.|
|
||||
|<a href="#interval">interval(cb, time) -> @Timer</a>|Runs the `cb` function every specified amount of `time`.|
|
||||
|<a href="#multiprompt">multiprompt(str)</a>|Changes the text prompt when Hilbish asks for more input.|
|
||||
|<a href="#prependPath">prependPath(dir)</a>|Prepends `dir` to $PATH.|
|
||||
|<a href="#prompt">prompt(str, typ)</a>|Changes the shell prompt to the provided string.|
|
||||
|<a href="#read">read(prompt) -> input (string)</a>|Read input from the user, using Hilbish's line editor/input reader.|
|
||||
|<a href="#timeout">timeout(cb, time) -> @Timer</a>|Executed the `cb` function after a period of `time`.|
|
||||
|<a href="#which">which(name) -> string</a>|Checks if `name` is a valid command.|
|
||||
|<a href="#runnerMode">runnerMode(mode)</a>|Sets the execution/runner mode for interactive Hilbish.|
|
||||
|<a href="#run">run(cmd, streams)</a>|Runs `cmd` in Hilbish's shell script interpreter.|
|
||||
|
||||
## Static module fields
|
||||
|||
|
||||
|----|----|
|
||||
|ver|The version of Hilbish|
|
||||
|goVersion|The version of Go that Hilbish was compiled with|
|
||||
|user|Username of the user|
|
||||
|host|Hostname of the machine|
|
||||
|dataDir|Directory for Hilbish data files, including the docs and default modules|
|
||||
|interactive|Is Hilbish in an interactive shell?|
|
||||
|login|Is Hilbish the login shell?|
|
||||
|vimMode|Current Vim input mode of Hilbish (will be nil if not in Vim input mode)|
|
||||
|exitCode|Exit code of the last executed command|
|
||||
|
||||
<hr>
|
||||
<div id='alias'>
|
||||
<h4 class='heading'>
|
||||
hilbish.alias(cmd, orig)
|
||||
<a href="#alias" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Sets an alias, with a name of `cmd` to another command.
|
||||
|
||||
#### Parameters
|
||||
`string` **`cmd`**
|
||||
Name of the alias
|
||||
|
||||
`string` **`orig`**
|
||||
Command that will be aliased
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- With this, "ga file" will turn into "git add file"
|
||||
hilbish.alias('ga', 'git add')
|
||||
|
||||
-- Numbered substitutions are supported here!
|
||||
hilbish.alias('dircount', 'ls %1 | wc -l')
|
||||
-- "dircount ~" would count how many files are in ~ (home directory).
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='appendPath'>
|
||||
<h4 class='heading'>
|
||||
hilbish.appendPath(dir)
|
||||
<a href="#appendPath" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Appends the provided dir to the command path (`$PATH`)
|
||||
|
||||
#### Parameters
|
||||
`string|table` **`dir`**
|
||||
Directory (or directories) to append to path
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
hilbish.appendPath '~/go/bin'
|
||||
-- Will add ~/go/bin to the command path.
|
||||
|
||||
-- Or do multiple:
|
||||
hilbish.appendPath {
|
||||
'~/go/bin',
|
||||
'~/.local/bin'
|
||||
}
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='complete'>
|
||||
<h4 class='heading'>
|
||||
hilbish.complete(scope, cb)
|
||||
<a href="#complete" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Registers a completion handler for the specified scope.
|
||||
A `scope` is expected to be `command.<cmd>`,
|
||||
replacing <cmd> with the name of the command (for example `command.git`).
|
||||
The documentation for completions, under Features/Completions or `doc completions`
|
||||
provides more details.
|
||||
|
||||
#### Parameters
|
||||
`string` **`scope`**
|
||||
|
||||
|
||||
`function` **`cb`**
|
||||
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- This is a very simple example. Read the full doc for completions for details.
|
||||
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||
if #fields == 0 then
|
||||
-- complete for commands
|
||||
local comps, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||
local compGroup = {
|
||||
items = comps, -- our list of items to complete
|
||||
type = 'grid' -- what our completions will look like.
|
||||
}
|
||||
|
||||
return {compGroup}, pfx
|
||||
end
|
||||
|
||||
-- otherwise just be boring and return files
|
||||
|
||||
local comps, pfx = hilbish.completion.files(query, ctx, fields)
|
||||
local compGroup = {
|
||||
items = comps,
|
||||
type = 'grid'
|
||||
}
|
||||
|
||||
return {compGroup}, pfx
|
||||
end)
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='cwd'>
|
||||
<h4 class='heading'>
|
||||
hilbish.cwd() -> string
|
||||
<a href="#cwd" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the current directory of the shell.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='exec'>
|
||||
<h4 class='heading'>
|
||||
hilbish.exec(cmd)
|
||||
<a href="#exec" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Replaces the currently running Hilbish instance with the supplied command.
|
||||
This can be used to do an in-place restart.
|
||||
|
||||
#### Parameters
|
||||
`string` **`cmd`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='goro'>
|
||||
<h4 class='heading'>
|
||||
hilbish.goro(fn)
|
||||
<a href="#goro" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Puts `fn` in a Goroutine.
|
||||
This can be used to run any function in another thread at the same time as other Lua code.
|
||||
**NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.**
|
||||
**This is a limitation of the Lua runtime.**
|
||||
|
||||
#### Parameters
|
||||
`function` **`fn`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='highlighter'>
|
||||
<h4 class='heading'>
|
||||
hilbish.highlighter(line)
|
||||
<a href="#highlighter" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Line highlighter handler.
|
||||
This is mainly for syntax highlighting, but in reality could set the input
|
||||
of the prompt to *display* anything. The callback is passed the current line
|
||||
and is expected to return a line that will be used as the input display.
|
||||
Note that to set a highlighter, one has to override this function.
|
||||
|
||||
#### Parameters
|
||||
`string` **`line`**
|
||||
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
--This code will highlight all double quoted strings in green.
|
||||
function hilbish.highlighter(line)
|
||||
|
||||
return line:gsub('"%w+"', function(c) return lunacolors.green(c) end)
|
||||
|
||||
end
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='hinter'>
|
||||
<h4 class='heading'>
|
||||
hilbish.hinter(line, pos)
|
||||
<a href="#hinter" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
The command line hint handler. It gets called on every key insert to
|
||||
determine what text to use as an inline hint. It is passed the current
|
||||
line and cursor position. It is expected to return a string which is used
|
||||
as the text for the hint. This is by default a shim. To set hints,
|
||||
override this function with your custom handler.
|
||||
|
||||
#### Parameters
|
||||
`string` **`line`**
|
||||
|
||||
|
||||
`number` **`pos`**
|
||||
Position of cursor in line. Usually equals string.len(line)
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- this will display "hi" after the cursor in a dimmed color.
|
||||
function hilbish.hinter(line, pos)
|
||||
return 'hi'
|
||||
end
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='inputMode'>
|
||||
<h4 class='heading'>
|
||||
hilbish.inputMode(mode)
|
||||
<a href="#inputMode" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Sets the input mode for Hilbish's line reader.
|
||||
`emacs` is the default. Setting it to `vim` changes behavior of input to be
|
||||
Vim-like with modes and Vim keybinds.
|
||||
|
||||
#### Parameters
|
||||
`string` **`mode`**
|
||||
Can be set to either `emacs` or `vim`
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='interval'>
|
||||
<h4 class='heading'>
|
||||
hilbish.interval(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||
<a href="#interval" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Runs the `cb` function every specified amount of `time`.
|
||||
This creates a timer that ticking immediately.
|
||||
|
||||
#### Parameters
|
||||
`function` **`cb`**
|
||||
|
||||
|
||||
`number` **`time`**
|
||||
Time in milliseconds.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='multiprompt'>
|
||||
<h4 class='heading'>
|
||||
hilbish.multiprompt(str)
|
||||
<a href="#multiprompt" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Changes the text prompt when Hilbish asks for more input.
|
||||
This will show up when text is incomplete, like a missing quote
|
||||
|
||||
#### Parameters
|
||||
`string` **`str`**
|
||||
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
--[[
|
||||
imagine this is your text input:
|
||||
user ~ ∆ echo "hey
|
||||
|
||||
but there's a missing quote! hilbish will now prompt you so the terminal
|
||||
will look like:
|
||||
user ~ ∆ echo "hey
|
||||
--> ...!"
|
||||
|
||||
so then you get
|
||||
user ~ ∆ echo "hey
|
||||
--> ...!"
|
||||
hey ...!
|
||||
]]--
|
||||
hilbish.multiprompt '-->'
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='prependPath'>
|
||||
<h4 class='heading'>
|
||||
hilbish.prependPath(dir)
|
||||
<a href="#prependPath" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Prepends `dir` to $PATH.
|
||||
|
||||
#### Parameters
|
||||
`string` **`dir`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='prompt'>
|
||||
<h4 class='heading'>
|
||||
hilbish.prompt(str, typ)
|
||||
<a href="#prompt" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Changes the shell prompt to the provided string.
|
||||
There are a few verbs that can be used in the prompt text.
|
||||
These will be formatted and replaced with the appropriate values.
|
||||
`%d` - Current working directory
|
||||
`%u` - Name of current user
|
||||
`%h` - Hostname of device
|
||||
|
||||
#### Parameters
|
||||
`string` **`str`**
|
||||
|
||||
|
||||
`string` **`typ?`**
|
||||
Type of prompt, being left or right. Left by default.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- the default hilbish prompt without color
|
||||
hilbish.prompt '%u %d ∆'
|
||||
-- or something of old:
|
||||
hilbish.prompt '%u@%h :%d $'
|
||||
-- prompt: user@hostname: ~/directory $
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='read'>
|
||||
<h4 class='heading'>
|
||||
hilbish.read(prompt) -> input (string)
|
||||
<a href="#read" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Read input from the user, using Hilbish's line editor/input reader.
|
||||
This is a separate instance from the one Hilbish actually uses.
|
||||
Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||
|
||||
#### Parameters
|
||||
`string` **`prompt?`**
|
||||
Text to print before input, can be empty.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='timeout'>
|
||||
<h4 class='heading'>
|
||||
hilbish.timeout(cb, time) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||
<a href="#timeout" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Executed the `cb` function after a period of `time`.
|
||||
This creates a Timer that starts ticking immediately.
|
||||
|
||||
#### Parameters
|
||||
`function` **`cb`**
|
||||
|
||||
|
||||
`number` **`time`**
|
||||
Time to run in milliseconds.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='which'>
|
||||
<h4 class='heading'>
|
||||
hilbish.which(name) -> string
|
||||
<a href="#which" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Checks if `name` is a valid command.
|
||||
Will return the path of the binary, or a basename if it's a commander.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
## Types
|
||||
<hr>
|
||||
|
||||
## Sink
|
||||
A sink is a structure that has input and/or output to/from a desination.
|
||||
|
||||
### Methods
|
||||
#### autoFlush(auto)
|
||||
Sets/toggles the option of automatically flushing output.
|
||||
A call with no argument will toggle the value.
|
||||
|
||||
#### flush()
|
||||
Flush writes all buffered input to the sink.
|
||||
|
||||
#### read() -> string
|
||||
Reads a liine of input from the sink.
|
||||
|
||||
#### readAll() -> string
|
||||
Reads all input from the sink.
|
||||
|
||||
#### write(str)
|
||||
Writes data to a sink.
|
||||
|
||||
#### writeln(str)
|
||||
Writes data to a sink with a newline at the end.
|
||||
|
||||
<hr>
|
||||
<div id='run'>
|
||||
<h4 class='heading'>
|
||||
hilbish.run(cmd, streams)
|
||||
<a href="#run" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Runs `cmd` in Hilbish's shell script interpreter.
|
||||
The `streams` parameter specifies the output and input streams the command should use.
|
||||
For example, to write command output to a sink.
|
||||
As a table, the caller can directly specify the standard output, error, and input
|
||||
streams of the command with the table keys `out`, `err`, and `input` respectively.
|
||||
As a boolean, it specifies whether the command should use standard output or return its output streams.
|
||||
#### Parameters
|
||||
`cmd` **`string`**
|
||||
|
||||
|
||||
`streams` **`table|boolean`**
|
||||
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- This code is the same as `ls -l | wc -l`
|
||||
local fs = require 'fs'
|
||||
local pr, pw = fs.pipe()
|
||||
hilbish.run('ls -l', {
|
||||
stdout = pw,
|
||||
stderr = pw,
|
||||
})
|
||||
pw:close()
|
||||
hilbish.run('wc -l', {
|
||||
stdin = pr
|
||||
})
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='runnerMode'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runnerMode(mode)
|
||||
<a href="#runnerMode" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Sets the execution/runner mode for interactive Hilbish.
|
||||
**NOTE: This function is deprecated and will be removed in 3.0**
|
||||
Use `hilbish.runner.setCurrent` instead.
|
||||
This determines whether Hilbish wll try to run input as Lua
|
||||
and/or sh or only do one of either.
|
||||
Accepted values for mode are hybrid (the default), hybridRev (sh first then Lua),
|
||||
sh, and lua. It also accepts a function, to which if it is passed one
|
||||
will call it to execute user input instead.
|
||||
Read [about runner mode](../features/runner-mode) for more information.
|
||||
#### Parameters
|
||||
`mode` **`string|function`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
67
docs/api/hilbish/hilbish.abbr.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Module hilbish.abbr
|
||||
description: command line abbreviations
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
|
||||
## Introduction
|
||||
The abbr module manages Hilbish abbreviations. These are words that can be replaced
|
||||
with longer command line strings when entered.
|
||||
As an example, `git push` can be abbreviated to `gp`. When the user types
|
||||
`gp` into the command line, after hitting space or enter, it will expand to `git push`.
|
||||
Abbreviations can be used as an alternative to aliases. They are saved entirely in the history
|
||||
Instead of the aliased form of the same command.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#remove">remove(abbr)</a>|Removes the named `abbr`.|
|
||||
|<a href="#add">add(abbr, expanded|function, opts)</a>|Adds an abbreviation. The `abbr` is the abbreviation itself,|
|
||||
<hr>
|
||||
<div id='add'>
|
||||
<h4 class='heading'>
|
||||
hilbish.abbr.add(abbr, expanded|function, opts)
|
||||
<a href="#add" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Adds an abbreviation. The `abbr` is the abbreviation itself,
|
||||
while `expanded` is what the abbreviation should expand to.
|
||||
It can be either a function or a string. If it is a function, it will expand to what
|
||||
the function returns.
|
||||
`opts` is a table that accepts 1 key: `anywhere`.
|
||||
`opts.anywhere` defines whether the abbr expands anywhere in the command line or not,
|
||||
whereas the default behavior is only at the beginning of the line
|
||||
#### Parameters
|
||||
`abbr` **`string`**
|
||||
|
||||
|
||||
`expanded|function` **`string`**
|
||||
|
||||
|
||||
`opts` **`table`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='remove'>
|
||||
<h4 class='heading'>
|
||||
hilbish.abbr.remove(abbr)
|
||||
<a href="#remove" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Removes the named `abbr`.
|
||||
#### Parameters
|
||||
`abbr` **`string`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
91
docs/api/hilbish/hilbish.aliases.md
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Module hilbish.aliases
|
||||
description: command aliasing
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
The alias interface deals with all command aliases in Hilbish.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#aliases.add">add(alias, cmd)</a>|This is an alias (ha) for the [hilbish.alias](../#alias) function.|
|
||||
|<a href="#aliases.delete">delete(name)</a>|Removes an alias.|
|
||||
|<a href="#aliases.list">list() -> table[string, string]</a>|Get a table of all aliases, with string keys as the alias and the value as the command.|
|
||||
|<a href="#aliases.resolve">resolve(alias) -> string?</a>|Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.|
|
||||
|
||||
<hr>
|
||||
<div id='aliases.add'>
|
||||
<h4 class='heading'>
|
||||
hilbish.aliases.add(alias, cmd)
|
||||
<a href="#aliases.add" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
This is an alias (ha) for the [hilbish.alias](../#alias) function.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='aliases.delete'>
|
||||
<h4 class='heading'>
|
||||
hilbish.aliases.delete(name)
|
||||
<a href="#aliases.delete" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Removes an alias.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='aliases.list'>
|
||||
<h4 class='heading'>
|
||||
hilbish.aliases.list() -> table[string, string]
|
||||
<a href="#aliases.list" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Get a table of all aliases, with string keys as the alias and the value as the command.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
#### Example
|
||||
```lua
|
||||
hilbish.aliases.add('hi', 'echo hi')
|
||||
|
||||
local aliases = hilbish.aliases.list()
|
||||
-- -> {hi = 'echo hi'}
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='aliases.resolve'>
|
||||
<h4 class='heading'>
|
||||
hilbish.aliases.resolve(alias) -> string?
|
||||
<a href="#aliases.resolve" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.
|
||||
|
||||
#### Parameters
|
||||
`string` **`alias`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
149
docs/api/hilbish/hilbish.completion.md
Normal file
@ -0,0 +1,149 @@
|
||||
---
|
||||
title: Module hilbish.completion
|
||||
description: tab completions
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
The completions interface deals with tab completions.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#completion.bins">bins(query, ctx, fields) -> entries (table), prefix (string)</a>|Return binaries/executables based on the provided parameters.|
|
||||
|<a href="#completion.call">call(name, query, ctx, fields) -> completionGroups (table), prefix (string)</a>|Calls a completer function. This is mainly used to call a command completer, which will have a `name`|
|
||||
|<a href="#completion.files">files(query, ctx, fields) -> entries (table), prefix (string)</a>|Returns file matches based on the provided parameters.|
|
||||
|<a href="#completion.handler">handler(line, pos)</a>|This function contains the general completion handler for Hilbish. This function handles|
|
||||
|
||||
<hr>
|
||||
<div id='completion.bins'>
|
||||
<h4 class='heading'>
|
||||
hilbish.completion.bins(query, ctx, fields) -> entries (table), prefix (string)
|
||||
<a href="#completion.bins" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Return binaries/executables based on the provided parameters.
|
||||
This function is meant to be used as a helper in a command completion handler.
|
||||
|
||||
#### Parameters
|
||||
`string` **`query`**
|
||||
|
||||
|
||||
`string` **`ctx`**
|
||||
|
||||
|
||||
`table` **`fields`**
|
||||
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- an extremely simple completer for sudo.
|
||||
hilbish.complete('command.sudo', function(query, ctx, fields)
|
||||
table.remove(fields, 1)
|
||||
if #fields[1] then
|
||||
-- return commands because sudo runs a command as root..!
|
||||
|
||||
local entries, pfx = hilbish.completion.bins(query, ctx, fields)
|
||||
return {
|
||||
type = 'grid',
|
||||
items = entries
|
||||
}, pfx
|
||||
end
|
||||
|
||||
-- ... else suggest files or anything else ..
|
||||
end)
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='completion.call'>
|
||||
<h4 class='heading'>
|
||||
hilbish.completion.call(name, query, ctx, fields) -> completionGroups (table), prefix (string)
|
||||
<a href="#completion.call" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Calls a completer function. This is mainly used to call a command completer, which will have a `name`
|
||||
in the form of `command.name`, example: `command.git`.
|
||||
You can check the Completions doc or `doc completions` for info on the `completionGroups` return value.
|
||||
|
||||
#### Parameters
|
||||
`string` **`name`**
|
||||
|
||||
|
||||
`string` **`query`**
|
||||
|
||||
|
||||
`string` **`ctx`**
|
||||
|
||||
|
||||
`table` **`fields`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='completion.files'>
|
||||
<h4 class='heading'>
|
||||
hilbish.completion.files(query, ctx, fields) -> entries (table), prefix (string)
|
||||
<a href="#completion.files" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns file matches based on the provided parameters.
|
||||
This function is meant to be used as a helper in a command completion handler.
|
||||
|
||||
#### Parameters
|
||||
`string` **`query`**
|
||||
|
||||
|
||||
`string` **`ctx`**
|
||||
|
||||
|
||||
`table` **`fields`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='completion.handler'>
|
||||
<h4 class='heading'>
|
||||
hilbish.completion.handler(line, pos)
|
||||
<a href="#completion.handler" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
This function contains the general completion handler for Hilbish. This function handles
|
||||
completion of everything, which includes calling other command handlers, binaries, and files.
|
||||
This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function.
|
||||
|
||||
#### Parameters
|
||||
`string` **`line`**
|
||||
The current Hilbish command line
|
||||
|
||||
`number` **`pos`**
|
||||
Numerical position of the cursor
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- stripped down version of the default implementation
|
||||
function hilbish.completion.handler(line, pos)
|
||||
local query = fields[#fields]
|
||||
|
||||
if #fields == 1 then
|
||||
-- call bins handler here
|
||||
else
|
||||
-- call command completer or files completer here
|
||||
end
|
||||
end
|
||||
```
|
||||
</div>
|
||||
|
||||
102
docs/api/hilbish/hilbish.history.md
Normal file
@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Module hilbish.history
|
||||
description: command history
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
The history interface deals with command history.
|
||||
This includes the ability to override functions to change the main
|
||||
method of saving history.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#history.add">add(cmd)</a>|Adds a command to the history.|
|
||||
|<a href="#history.all">all() -> table</a>|Retrieves all history as a table.|
|
||||
|<a href="#history.clear">clear()</a>|Deletes all commands from the history.|
|
||||
|<a href="#history.get">get(index)</a>|Retrieves a command from the history based on the `index`.|
|
||||
|<a href="#history.size">size() -> number</a>|Returns the amount of commands in the history.|
|
||||
|
||||
<hr>
|
||||
<div id='history.add'>
|
||||
<h4 class='heading'>
|
||||
hilbish.history.add(cmd)
|
||||
<a href="#history.add" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Adds a command to the history.
|
||||
|
||||
#### Parameters
|
||||
`string` **`cmd`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='history.all'>
|
||||
<h4 class='heading'>
|
||||
hilbish.history.all() -> table
|
||||
<a href="#history.all" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Retrieves all history as a table.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='history.clear'>
|
||||
<h4 class='heading'>
|
||||
hilbish.history.clear()
|
||||
<a href="#history.clear" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Deletes all commands from the history.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='history.get'>
|
||||
<h4 class='heading'>
|
||||
hilbish.history.get(index)
|
||||
<a href="#history.get" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Retrieves a command from the history based on the `index`.
|
||||
|
||||
#### Parameters
|
||||
`number` **`index`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='history.size'>
|
||||
<h4 class='heading'>
|
||||
hilbish.history.size() -> number
|
||||
<a href="#history.size" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the amount of commands in the history.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
146
docs/api/hilbish/hilbish.jobs.md
Normal file
@ -0,0 +1,146 @@
|
||||
---
|
||||
title: Module hilbish.jobs
|
||||
description: background job management
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Manage interactive jobs in Hilbish via Lua.
|
||||
|
||||
Jobs are the name of background tasks/commands. A job can be started via
|
||||
interactive usage or with the functions defined below for use in external runners.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#jobs.add">add(cmdstr, args, execPath)</a>|Creates a new job. This function does not run the job. This function is intended to be|
|
||||
|<a href="#jobs.all">all() -> table[@Job]</a>|Returns a table of all job objects.|
|
||||
|<a href="#jobs.disown">disown(id)</a>|Disowns a job. This simply deletes it from the list of jobs without stopping it.|
|
||||
|<a href="#jobs.get">get(id) -> @Job</a>|Get a job object via its ID.|
|
||||
|<a href="#jobs.last">last() -> @Job</a>|Returns the last added job to the table.|
|
||||
|
||||
<hr>
|
||||
<div id='jobs.add'>
|
||||
<h4 class='heading'>
|
||||
hilbish.jobs.add(cmdstr, args, execPath)
|
||||
<a href="#jobs.add" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Creates a new job. This function does not run the job. This function is intended to be
|
||||
used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs.
|
||||
|
||||
#### Parameters
|
||||
`string` **`cmdstr`**
|
||||
String that a user would write for the job
|
||||
|
||||
`table` **`args`**
|
||||
Arguments for the commands. Has to include the name of the command.
|
||||
|
||||
`string` **`execPath`**
|
||||
Binary to use to run the command. Needs to be an absolute path.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
hilbish.jobs.add('go build', {'go', 'build'}, '/usr/bin/go')
|
||||
```
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='jobs.all'>
|
||||
<h4 class='heading'>
|
||||
hilbish.jobs.all() -> table[<a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;" id="lol">Job</a>]
|
||||
<a href="#jobs.all" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns a table of all job objects.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='jobs.disown'>
|
||||
<h4 class='heading'>
|
||||
hilbish.jobs.disown(id)
|
||||
<a href="#jobs.disown" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Disowns a job. This simply deletes it from the list of jobs without stopping it.
|
||||
|
||||
#### Parameters
|
||||
`number` **`id`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='jobs.get'>
|
||||
<h4 class='heading'>
|
||||
hilbish.jobs.get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;" id="lol">Job</a>
|
||||
<a href="#jobs.get" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Get a job object via its ID.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='jobs.last'>
|
||||
<h4 class='heading'>
|
||||
hilbish.jobs.last() -> <a href="/Hilbish/docs/api/hilbish/hilbish.jobs/#job" style="text-decoration: none;" id="lol">Job</a>
|
||||
<a href="#jobs.last" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the last added job to the table.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
## Types
|
||||
<hr>
|
||||
|
||||
## Job
|
||||
The Job type describes a Hilbish job.
|
||||
## Object properties
|
||||
|||
|
||||
|----|----|
|
||||
|cmd|The user entered command string for the job.|
|
||||
|running|Whether the job is running or not.|
|
||||
|id|The ID of the job in the job table|
|
||||
|pid|The Process ID|
|
||||
|exitCode|The last exit code of the job.|
|
||||
|stdout|The standard output of the job. This just means the normal logs of the process.|
|
||||
|stderr|The standard error stream of the process. This (usually) includes error messages of the job.|
|
||||
|
||||
|
||||
### Methods
|
||||
#### background()
|
||||
Puts a job in the background. This acts the same as initially running a job.
|
||||
|
||||
#### foreground()
|
||||
Puts a job in the foreground. This will cause it to run like it was
|
||||
executed normally and wait for it to complete.
|
||||
|
||||
#### start()
|
||||
Starts running the job.
|
||||
|
||||
#### stop()
|
||||
Stops the job from running.
|
||||
|
||||
135
docs/api/hilbish/hilbish.messages.md
Normal file
@ -0,0 +1,135 @@
|
||||
---
|
||||
title: Module hilbish.messages
|
||||
description: simplistic message passing
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
|
||||
## Introduction
|
||||
The messages interface defines a way for Hilbish-integrated commands,
|
||||
user config and other tasks to send notifications to alert the user.z
|
||||
The `hilbish.message` type is a table with the following keys:
|
||||
`title` (string): A title for the message notification.
|
||||
`text` (string): The contents of the message.
|
||||
`channel` (string): States the origin of the message, `hilbish.*` is reserved for Hilbish tasks.
|
||||
`summary` (string): A short summary of the `text`.
|
||||
`icon` (string): Unicode (preferably standard emoji) icon for the message notification
|
||||
`read` (boolean): Whether the full message has been read or not.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#unreadCount">unreadCount()</a>|Returns the amount of unread messages.|
|
||||
|<a href="#send">send(message)</a>|Sends a message.|
|
||||
|<a href="#readAll">readAll()</a>|Marks all messages as read.|
|
||||
|<a href="#read">read(idx)</a>|Marks a message at `idx` as read.|
|
||||
|<a href="#delete">delete(idx)</a>|Deletes the message at `idx`.|
|
||||
|<a href="#clear">clear()</a>|Deletes all messages.|
|
||||
|<a href="#all">all()</a>|Returns all messages.|
|
||||
<hr>
|
||||
<div id='all'>
|
||||
<h4 class='heading'>
|
||||
hilbish.messages.all()
|
||||
<a href="#all" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns all messages.
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='clear'>
|
||||
<h4 class='heading'>
|
||||
hilbish.messages.clear()
|
||||
<a href="#clear" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Deletes all messages.
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='delete'>
|
||||
<h4 class='heading'>
|
||||
hilbish.messages.delete(idx)
|
||||
<a href="#delete" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Deletes the message at `idx`.
|
||||
#### Parameters
|
||||
`idx` **`number`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='read'>
|
||||
<h4 class='heading'>
|
||||
hilbish.messages.read(idx)
|
||||
<a href="#read" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Marks a message at `idx` as read.
|
||||
#### Parameters
|
||||
`idx` **`number`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='readAll'>
|
||||
<h4 class='heading'>
|
||||
hilbish.messages.readAll()
|
||||
<a href="#readAll" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Marks all messages as read.
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='send'>
|
||||
<h4 class='heading'>
|
||||
hilbish.messages.send(message)
|
||||
<a href="#send" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Sends a message.
|
||||
#### Parameters
|
||||
`message` **`hilbish.message`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='unreadCount'>
|
||||
<h4 class='heading'>
|
||||
hilbish.messages.unreadCount()
|
||||
<a href="#unreadCount" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the amount of unread messages.
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
73
docs/api/hilbish/hilbish.module.md
Normal file
@ -0,0 +1,73 @@
|
||||
---
|
||||
title: Module hilbish.module
|
||||
description: native module loading
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The hilbish.module interface provides a function to load
|
||||
Hilbish plugins/modules. Hilbish modules are Go-written
|
||||
plugins (see https://pkg.go.dev/plugin) that are used to add functionality
|
||||
to Hilbish that cannot be written in Lua for any reason.
|
||||
|
||||
Note that you don't ever need to use the load function that is here as
|
||||
modules can be loaded with a `require` call like Lua C modules, and the
|
||||
search paths can be changed with the `paths` property here.
|
||||
|
||||
To make a valid native module, the Go plugin has to export a Loader function
|
||||
with a signature like so: `func(*rt.Runtime) rt.Value`.
|
||||
|
||||
`rt` in this case refers to the Runtime type at
|
||||
https://pkg.go.dev/github.com/arnodel/golua@master/runtime#Runtime
|
||||
|
||||
Hilbish uses this package as its Lua runtime. You will need to read
|
||||
it to use it for a native plugin.
|
||||
|
||||
Here is some code for an example plugin:
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
)
|
||||
|
||||
func Loader(rtm *rt.Runtime) rt.Value {
|
||||
return rt.StringValue("hello world!")
|
||||
}
|
||||
```
|
||||
|
||||
This can be compiled with `go build -buildmode=plugin plugin.go`.
|
||||
If you attempt to require and print the result (`print(require 'plugin')`), it will show "hello world!"
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#module.load">load(path)</a>|Loads a module at the designated `path`.|
|
||||
|
||||
## Static module fields
|
||||
|||
|
||||
|----|----|
|
||||
|paths|A list of paths to search when loading native modules. This is in the style of Lua search paths and will be used when requiring native modules. Example: `?.so;?/?.so`|
|
||||
|
||||
<hr>
|
||||
<div id='module.load'>
|
||||
<h4 class='heading'>
|
||||
hilbish.module.load(path)
|
||||
<a href="#module.load" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Loads a module at the designated `path`.
|
||||
It will throw if any error occurs.
|
||||
|
||||
#### Parameters
|
||||
`string` **`path`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
20
docs/api/hilbish/hilbish.os.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Module hilbish.os
|
||||
description: operating system info
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
Provides simple text information properties about the current operating system.
|
||||
This mainly includes the name and version.
|
||||
|
||||
## Static module fields
|
||||
|||
|
||||
|----|----|
|
||||
|family|Family name of the current OS|
|
||||
|name|Pretty name of the current OS|
|
||||
|version|Version of the current OS|
|
||||
|
||||
39
docs/api/hilbish/hilbish.processors.md
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Module hilbish.processors
|
||||
description: No description.
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
<hr>
|
||||
<div id='add'>
|
||||
<h4 class='heading'>
|
||||
hilbish.processors.add()
|
||||
<a href="#add" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='execute'>
|
||||
<h4 class='heading'>
|
||||
hilbish.processors.execute()
|
||||
<a href="#execute" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Run all command processors, in order by priority.
|
||||
It returns the processed command (which may be the same as the passed command)
|
||||
and a boolean which states whether to proceed with command execution.
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
247
docs/api/hilbish/hilbish.runner.md
Normal file
@ -0,0 +1,247 @@
|
||||
---
|
||||
title: Module hilbish.runner
|
||||
description: interactive command runner customization
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
The runner interface contains functions that allow the user to change
|
||||
how Hilbish interprets interactive input.
|
||||
Users can add and change the default runner for interactive input to any
|
||||
language or script of their choosing. A good example is using it to
|
||||
write command in Fennel.
|
||||
|
||||
Runners are functions that evaluate user input. The default runners in
|
||||
Hilbish can run shell script and Lua code.
|
||||
|
||||
A runner is passed the input and has to return a table with these values.
|
||||
All are not required, only the useful ones the runner needs to return.
|
||||
(So if there isn't an error, just omit `err`.)
|
||||
|
||||
- `exitCode` (number): Exit code of the command
|
||||
- `input` (string): The text input of the user. This is used by Hilbish to append extra input, in case
|
||||
more is requested.
|
||||
- `err` (string): A string that represents an error from the runner.
|
||||
This should only be set when, for example, there is a syntax error.
|
||||
It can be set to a few special values for Hilbish to throw the right
|
||||
hooks and have a better looking message.
|
||||
- `\<command>: not-found` will throw a `command.not-found` hook
|
||||
based on what `\<command>` is.
|
||||
- `\<command>: not-executable` will throw a `command.not-executable` hook.
|
||||
- `continue` (boolean): Whether Hilbish should prompt the user for no input
|
||||
- `newline` (boolean): Whether a newline should be added at the end of `input`.
|
||||
|
||||
Here is a simple example of a fennel runner. It falls back to
|
||||
shell script if fennel eval has an error.
|
||||
```lua
|
||||
local fennel = require 'fennel'
|
||||
|
||||
hilbish.runnerMode(function(input)
|
||||
local ok = pcall(fennel.eval, input)
|
||||
if ok then
|
||||
return {
|
||||
input = input
|
||||
}
|
||||
end
|
||||
|
||||
return hilbish.runner.sh(input)
|
||||
end)
|
||||
```
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#runner.lua">lua(cmd)</a>|Evaluates `cmd` as Lua input. This is the same as using `dofile`|
|
||||
|<a href="#sh">sh()</a>|nil|
|
||||
|<a href="#setMode">setMode(mode)</a>|**NOTE: This function is deprecated and will be removed in 3.0**|
|
||||
|<a href="#setCurrent">setCurrent(name)</a>|Sets Hilbish's runner mode by name.|
|
||||
|<a href="#set">set(name, runner)</a>|*Sets* a runner by name. The difference between this function and|
|
||||
|<a href="#run">run(input, priv)</a>|Runs `input` with the currently set Hilbish runner.|
|
||||
|<a href="#getCurrent">getCurrent()</a>|Returns the current runner by name.|
|
||||
|<a href="#get">get(name)</a>|Get a runner by name.|
|
||||
|<a href="#exec">exec(cmd, runnerName)</a>|Executes `cmd` with a runner.|
|
||||
|<a href="#add">add(name, runner)</a>|Adds a runner to the table of available runners.|
|
||||
|
||||
<hr>
|
||||
<div id='runner.lua'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.lua(cmd)
|
||||
<a href="#runner.lua" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Evaluates `cmd` as Lua input. This is the same as using `dofile`
|
||||
or `load`, but is appropriated for the runner interface.
|
||||
|
||||
#### Parameters
|
||||
`string` **`cmd`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='add'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.add(name, runner)
|
||||
<a href="#add" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Adds a runner to the table of available runners.
|
||||
If runner is a table, it must have the run function in it.
|
||||
#### Parameters
|
||||
`name` **`string`**
|
||||
Name of the runner
|
||||
|
||||
`runner` **`function|table`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='exec'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.exec(cmd, runnerName)
|
||||
<a href="#exec" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Executes `cmd` with a runner.
|
||||
If `runnerName` is not specified, it uses the default Hilbish runner.
|
||||
#### Parameters
|
||||
`cmd` **`string`**
|
||||
|
||||
|
||||
`runnerName` **`string?`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='get'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.get(name)
|
||||
<a href="#get" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Get a runner by name.
|
||||
#### Parameters
|
||||
`name` **`string`**
|
||||
Name of the runner to retrieve.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='getCurrent'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.getCurrent()
|
||||
<a href="#getCurrent" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Returns the current runner by name.
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='run'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.run(input, priv)
|
||||
<a href="#run" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Runs `input` with the currently set Hilbish runner.
|
||||
This method is how Hilbish executes commands.
|
||||
`priv` is an optional boolean used to state if the input should be saved to history.
|
||||
#### Parameters
|
||||
`input` **`string`**
|
||||
|
||||
|
||||
`priv` **`bool`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='set'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.set(name, runner)
|
||||
<a href="#set" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
*Sets* a runner by name. The difference between this function and
|
||||
add, is set will *not* check if the named runner exists.
|
||||
The runner table must have the run function in it.
|
||||
#### Parameters
|
||||
`name` **`string`**
|
||||
|
||||
|
||||
`runner` **`table`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='setCurrent'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.setCurrent(name)
|
||||
<a href="#setCurrent" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Sets Hilbish's runner mode by name.
|
||||
#### Parameters
|
||||
`name` **`string`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='setMode'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.setMode(mode)
|
||||
<a href="#setMode" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
**NOTE: This function is deprecated and will be removed in 3.0**
|
||||
Use `hilbish.runner.setCurrent` instead.
|
||||
This is the same as the `hilbish.runnerMode` function.
|
||||
It takes a callback, which will be used to execute all interactive input.
|
||||
Or a string which names the runner mode to use.
|
||||
#### Parameters
|
||||
`mode` **`string|function`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='sh'>
|
||||
<h4 class='heading'>
|
||||
hilbish.runner.sh()
|
||||
<a href="#sh" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
100
docs/api/hilbish/hilbish.timers.md
Normal file
@ -0,0 +1,100 @@
|
||||
---
|
||||
title: Module hilbish.timers
|
||||
description: timeout and interval API
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
If you ever want to run a piece of code on a timed interval, or want to wait
|
||||
a few seconds, you don't have to rely on timing tricks, as Hilbish has a
|
||||
timer API to set intervals and timeouts.
|
||||
|
||||
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
|
||||
accessible with `doc hilbish`, or `Module hilbish` on the Website).
|
||||
|
||||
An example of usage:
|
||||
```lua
|
||||
local t = hilbish.timers.create(hilbish.timers.TIMEOUT, 5000, function()
|
||||
print 'hello!'
|
||||
end)
|
||||
|
||||
t:start()
|
||||
print(t.running) // true
|
||||
```
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#timers.create">create(type, time, callback) -> @Timer</a>|Creates a timer that runs based on the specified `time`.|
|
||||
|<a href="#timers.get">get(id) -> @Timer</a>|Retrieves a timer via its ID.|
|
||||
|
||||
## Static module fields
|
||||
|||
|
||||
|----|----|
|
||||
|INTERVAL|Constant for an interval timer type|
|
||||
|TIMEOUT|Constant for a timeout timer type|
|
||||
|
||||
<hr>
|
||||
<div id='timers.create'>
|
||||
<h4 class='heading'>
|
||||
hilbish.timers.create(type, time, callback) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||
<a href="#timers.create" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Creates a timer that runs based on the specified `time`.
|
||||
|
||||
#### Parameters
|
||||
`number` **`type`**
|
||||
What kind of timer to create, can either be `hilbish.timers.INTERVAL` or `hilbish.timers.TIMEOUT`
|
||||
|
||||
`number` **`time`**
|
||||
The amount of time the function should run in milliseconds.
|
||||
|
||||
`function` **`callback`**
|
||||
The function to run for the timer.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='timers.get'>
|
||||
<h4 class='heading'>
|
||||
hilbish.timers.get(id) -> <a href="/Hilbish/docs/api/hilbish/hilbish.timers/#timer" style="text-decoration: none;" id="lol">Timer</a>
|
||||
<a href="#timers.get" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Retrieves a timer via its ID.
|
||||
|
||||
#### Parameters
|
||||
`number` **`id`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
## Types
|
||||
<hr>
|
||||
|
||||
## Timer
|
||||
The Job type describes a Hilbish timer.
|
||||
## Object properties
|
||||
|||
|
||||
|----|----|
|
||||
|type|What type of timer it is|
|
||||
|running|If the timer is running|
|
||||
|duration|The duration in milliseconds that the timer will run|
|
||||
|
||||
|
||||
### Methods
|
||||
#### start()
|
||||
Starts a timer.
|
||||
|
||||
#### stop()
|
||||
Stops a timer.
|
||||
|
||||
20
docs/api/hilbish/hilbish.userDir.md
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Module hilbish.userDir
|
||||
description: user-related directories
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
This interface just contains properties to know about certain user directories.
|
||||
It is equivalent to XDG on Linux and gets the user's preferred directories
|
||||
for configs and data.
|
||||
|
||||
## Static module fields
|
||||
|||
|
||||
|----|----|
|
||||
|config|The user's config directory|
|
||||
|data|The user's directory for program data|
|
||||
|
||||
66
docs/api/readline.md
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
title: Module readline
|
||||
description: line reader library
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
The readline module is responsible for reading input from the user.
|
||||
The readline module is what Hilbish uses to read input from the user,
|
||||
including all the interactive features of Hilbish like history search,
|
||||
syntax highlighting, everything. The global Hilbish readline instance
|
||||
is usable at `hilbish.editor`.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#New">new() -> @Readline</a>|Creates a new readline instance.|
|
||||
|
||||
<hr>
|
||||
<div id='New'>
|
||||
<h4 class='heading'>
|
||||
readline.new() -> <a href="/Hilbish/docs/api/readline/#readline" style="text-decoration: none;" id="lol">Readline</a>
|
||||
<a href="#New" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Creates a new readline instance.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
## Types
|
||||
<hr>
|
||||
|
||||
## Readline
|
||||
|
||||
### Methods
|
||||
#### deleteByAmount(amount)
|
||||
Deletes characters in the line by the given amount.
|
||||
|
||||
#### getLine() -> string
|
||||
Returns the current input line.
|
||||
|
||||
#### getVimRegister(register) -> string
|
||||
Returns the text that is at the register.
|
||||
|
||||
#### insert(text)
|
||||
Inserts text into the Hilbish command line.
|
||||
|
||||
#### log(text)
|
||||
Prints a message *before* the prompt without it being interrupted by user input.
|
||||
|
||||
#### read() -> string
|
||||
Reads input from the user.
|
||||
|
||||
#### getChar() -> string
|
||||
Reads a keystroke from the user. This is in a format of something like Ctrl-L.
|
||||
|
||||
#### setVimRegister(register, text)
|
||||
Sets the vim register at `register` to hold the passed text.
|
||||
|
||||
50
docs/api/snail.md
Normal file
@ -0,0 +1,50 @@
|
||||
---
|
||||
title: Module snail
|
||||
description: shell script interpreter library
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The snail library houses Hilbish's Lua wrapper of its shell script interpreter.
|
||||
It's not very useful other than running shell scripts, which can be done with other
|
||||
Hilbish functions.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#new">new() -> @Snail</a>|Creates a new Snail instance.|
|
||||
|
||||
<hr>
|
||||
<div id='new'>
|
||||
<h4 class='heading'>
|
||||
snail.new() -> <a href="/Hilbish/docs/api/snail/#snail" style="text-decoration: none;" id="lol">Snail</a>
|
||||
<a href="#new" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Creates a new Snail instance.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
## Types
|
||||
<hr>
|
||||
|
||||
## Snail
|
||||
A Snail is a shell script interpreter instance.
|
||||
|
||||
### Methods
|
||||
#### dir(path)
|
||||
Changes the directory of the snail instance.
|
||||
The interpreter keeps its set directory even when the Hilbish process changes
|
||||
directory, so this should be called on the `hilbish.cd` hook.
|
||||
|
||||
#### run(command, streams)
|
||||
Runs a shell command. Works the same as `hilbish.run`, but only accepts a table of streams.
|
||||
|
||||
81
docs/api/terminal.md
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Module terminal
|
||||
description: low level terminal library
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
The terminal library is a simple and lower level library for certain terminal interactions.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#restoreState">restoreState()</a>|Restores the last saved state of the terminal|
|
||||
|<a href="#saveState">saveState()</a>|Saves the current state of the terminal.|
|
||||
|<a href="#setRaw">setRaw()</a>|Puts the terminal into raw mode.|
|
||||
|<a href="#size">size()</a>|Gets the dimensions of the terminal. Returns a table with `width` and `height`|
|
||||
|
||||
<hr>
|
||||
<div id='restoreState'>
|
||||
<h4 class='heading'>
|
||||
terminal.restoreState()
|
||||
<a href="#restoreState" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Restores the last saved state of the terminal
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='saveState'>
|
||||
<h4 class='heading'>
|
||||
terminal.saveState()
|
||||
<a href="#saveState" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Saves the current state of the terminal.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='setRaw'>
|
||||
<h4 class='heading'>
|
||||
terminal.setRaw()
|
||||
<a href="#setRaw" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Puts the terminal into raw mode.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='size'>
|
||||
<h4 class='heading'>
|
||||
terminal.size()
|
||||
<a href="#size" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
||||
NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
51
docs/api/yarn.md
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Module yarn
|
||||
description: multi threading library
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "API"
|
||||
---
|
||||
|
||||
## Introduction
|
||||
Yarn is a simple multithreading library. Threads are individual Lua states,
|
||||
so they do NOT share the same environment as the code that runs the thread.
|
||||
Bait and Commanders are shared though, so you *can* throw hooks from 1 thread to another.
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
local yarn = require 'yarn'
|
||||
|
||||
-- calling t will run the yarn thread.
|
||||
local t = yarn.thread(print)
|
||||
t 'printing from another lua state!'
|
||||
```
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#thread">thread(fun) -> @Thread</a>|Creates a new, fresh Yarn thread.|
|
||||
|
||||
<hr>
|
||||
<div id='thread'>
|
||||
<h4 class='heading'>
|
||||
yarn.thread(fun) -> <a href="/Hilbish/docs/api/yarn/#thread" style="text-decoration: none;" id="lol">Thread</a>
|
||||
<a href="#thread" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Creates a new, fresh Yarn thread.
|
||||
`fun` is the function that will run in the thread.
|
||||
|
||||
#### Parameters
|
||||
This function has no parameters.
|
||||
</div>
|
||||
|
||||
## Types
|
||||
<hr>
|
||||
|
||||
## Thread
|
||||
|
||||
### Methods
|
||||
122
docs/completions.md
Normal file
@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Completions
|
||||
description: Tab completion for commands.
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Features"
|
||||
---
|
||||
|
||||
Completions for commands can be created with the [`hilbish.complete`](../api/hilbish#complete)
|
||||
function. See the link for how to use it.
|
||||
|
||||
To create completions for a command is simple.
|
||||
The callback will be passed 3 parameters:
|
||||
- `query` (string): The text that the user is currently trying to complete.
|
||||
This should be used to match entries.
|
||||
- `ctx` (string): Contains the entire line. Use this if
|
||||
more text is needed to be parsed for context.
|
||||
- `fields` (string): The `ctx` split up by spaces.
|
||||
|
||||
In most cases, the completer just uses `fields` to check the amount
|
||||
and `query` on what to match entries on.
|
||||
|
||||
In order to return your results, it has to go within a "completion group."
|
||||
Then you return a table of completion groups and a prefix. The prefix will
|
||||
usually just be the `query`.
|
||||
|
||||
Hilbish allows one to mix completion menus of different types, so
|
||||
a grid menu and a list menu can be used and complete and display at the same time.
|
||||
A completion group is a table with these keys:
|
||||
- `type` (string): type of completion menu, either `grid` or `list`.
|
||||
- `items` (table): a list of items.
|
||||
|
||||
The requirements of the `items` table is different based on the
|
||||
`type`. If it is a `grid`, it can simply be a table of strings.
|
||||
|
||||
Otherwise if it is a `list` then each entry can
|
||||
either be a string or a table.
|
||||
Example:
|
||||
```lua
|
||||
local cg = {
|
||||
items = {
|
||||
'list item 1',
|
||||
['--command-flag-here'] = {'this does a thing', '--the-flag-alias'}
|
||||
},
|
||||
type = 'list'
|
||||
}
|
||||
local cg2 = {
|
||||
items = {'just', 'a bunch', 'of items', 'here', 'hehe'},
|
||||
type = 'grid'
|
||||
}
|
||||
|
||||
return {cg, cg2}, prefix
|
||||
```
|
||||
|
||||
Which looks like this:
|
||||
{{< video src="https://safe.saya.moe/t4CiLK6dgPbD.mp4" >}}
|
||||
|
||||
# Completion Group Types
|
||||
### grid
|
||||
Grid is the simplest completion group type. All items are strings and when
|
||||
completion is done is displayed in a grid based on size.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
{
|
||||
items = {'just', 'a bunch', 'of items', 'here', 'hehe'},
|
||||
type = 'grid'
|
||||
}
|
||||
```
|
||||
|
||||
### list
|
||||
The list completion group type displays in a list. A list item can either be a string, or a table for additional display options.
|
||||
A completion alias can be specified either as the `2nd` entry in the options table
|
||||
or te `alias` key.
|
||||
|
||||
A description can optionally be displayed for a list item, which is either the `1st`
|
||||
entry or the `description` key.
|
||||
|
||||
Lastly, list entries can be styled. This is done with the `display` key. If this is present, this
|
||||
overrides what the completion item *looks* like.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
{
|
||||
items = {
|
||||
['--flag'] = {
|
||||
description = 'this flag nukes the bri ish',
|
||||
alias = '--bye-bri-ish',
|
||||
display = lunacolors.format('--{blue}fl{red}ag')
|
||||
},
|
||||
['--flag2'] = {
|
||||
'make pizza', -- description
|
||||
'--pizzuh', -- alias
|
||||
display = lunacolors.yellow '--pizzuh'
|
||||
},
|
||||
'--flag3'
|
||||
},
|
||||
type = 'list'
|
||||
}
|
||||
```
|
||||
|
||||
# Completion Handler
|
||||
Like most parts of Hilbish, it's made to be extensible and
|
||||
customizable. The default handler for completions in general can
|
||||
be overwritten to provide more advanced completions if needed.
|
||||
This usually doesn't need to be done though, unless you know
|
||||
what you're doing.
|
||||
|
||||
The default completion handler provides 3 things:
|
||||
binaries (with a plain name requested to complete, those in
|
||||
$PATH), files, or command completions. It will try to run a handler
|
||||
for the command or fallback to file completions.
|
||||
|
||||
To overwrite it, just assign a function to `hilbish.completion.handler` like so:
|
||||
```lua
|
||||
-- line is the entire line as a string
|
||||
-- pos is the position of the cursor.
|
||||
function hilbish.completion.handler(line, pos)
|
||||
-- do things
|
||||
end
|
||||
```
|
||||
26
docs/faq.md
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
title: Frequently Asked Questions
|
||||
layout: doc
|
||||
weight: -20
|
||||
menu: docs
|
||||
---
|
||||
|
||||
# Is Hilbish POSIX compliant?
|
||||
No, it is not. POSIX compliance is a non-goal. Perhaps in the future,
|
||||
someone would be able to write a native plugin to support shell scripting
|
||||
(which would be against it's main goal, but ....)
|
||||
|
||||
# Windows Support?
|
||||
It compiles for Windows (CI ensures it does), but otherwise it is not
|
||||
directly supported. If you'd like to improve this situation,
|
||||
checkout [the discussion](https://github.com/Rosettea/Hilbish/discussions/165).
|
||||
|
||||
# Why?
|
||||
Hilbish emerged from the desire of a Lua configured shell.
|
||||
It was the initial reason that it was created, but now it's more:
|
||||
to be hyper extensible, simpler and more user friendly.
|
||||
|
||||
# Does it have "autocompletion" or "tab completion"
|
||||
Of course! This is a modern shell. Hilbish provides a way for users
|
||||
to write tab completion for any command and/or the whole shell.
|
||||
Inline hinting and syntax highlighting are also available.
|
||||
11
docs/features/_index.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Features
|
||||
layout: doc
|
||||
weight: -40
|
||||
menu: docs
|
||||
---
|
||||
|
||||
Hilbish has a wide range of features to enhance the user's experience
|
||||
new ones are always being added. If there is something missing here or
|
||||
something you would like to see, please [start a discussion](https://github.com/Rosettea/Hilbish/discussions)
|
||||
or comment on any existing ones which match your request.
|
||||
39
docs/features/notifications.md
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Notification
|
||||
description: Get notified of shell actions.
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Features"
|
||||
---
|
||||
|
||||
Hilbish features a simple notification system which can be
|
||||
used by other plugins and parts of the shell to notify the user
|
||||
of various actions. This is used via the `hilbish.message` interface.
|
||||
|
||||
A `message` is defined as a table with the following properties:
|
||||
- `icon`: A unicode/emoji icon for the notification.
|
||||
- `title`: The title of the message
|
||||
- `text`: Message text/body
|
||||
- `channel`: The source of the message. This should be a
|
||||
unique and easily readable text identifier.
|
||||
- `summary`: A short summary of the notification and message.
|
||||
If this is not present and you are using this to display messages,
|
||||
you should take part of the `text` instead.
|
||||
|
||||
The `hilbish.message` interface provides the following functions:
|
||||
- `send(message)`: Sends a message and emits the `hilbish.notification`
|
||||
signal. DO NOT emit the `hilbish.notification` signal directly, or
|
||||
the message will not be stored by the message handler.
|
||||
- `read(idx)`: Marks message at `idx` as read.
|
||||
- `delete(idx)`: Removes message at `idx`.
|
||||
- `readAll()`: Marks all messages as read.
|
||||
- `clear()`: Deletes all messages.
|
||||
|
||||
There are a few simple use cases of this notification/messaging system.
|
||||
It could also be used as some "inter-shell" messaging system (???) but
|
||||
is intended to display to users.
|
||||
|
||||
An example is notifying users of completed jobs/commands ran in the background.
|
||||
Any Hilbish-native command (think the upcoming Greenhouse pager) can display
|
||||
it.
|
||||
83
docs/features/opts.md
Normal file
@ -0,0 +1,83 @@
|
||||
---
|
||||
title: Options
|
||||
description: Simple customizable options.
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Features"
|
||||
---
|
||||
|
||||
Opts are simple toggle or value options a user can set in Hilbish.
|
||||
As toggles, there are things like `autocd` or history saving. As values,
|
||||
there is the `motd` which the user can either change to a custom string or disable.
|
||||
|
||||
Opts are accessed from the `hilbish.opts` table. Here they can either
|
||||
be read or modified
|
||||
|
||||
### `autocd`
|
||||
#### Value: `boolean`
|
||||
#### Default: `false`
|
||||
|
||||
The autocd opt makes it so that lone directories attempted to be executed are
|
||||
instead set as the shell's directory.
|
||||
|
||||
Example:
|
||||
```
|
||||
~/Directory
|
||||
∆ ~
|
||||
~
|
||||
∆ Downloads
|
||||
~/Downloads
|
||||
∆ ../Documents
|
||||
~/Documents
|
||||
∆
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
### `history`
|
||||
#### Value: `boolean`
|
||||
#### Default: `true`
|
||||
Sets whether command history will be saved or not.
|
||||
|
||||
<hr>
|
||||
|
||||
### `greeting`
|
||||
#### Value: `boolean` or `string`
|
||||
The greeting is the message that Hilbish shows on startup
|
||||
(the one which says Welcome to Hilbish).
|
||||
|
||||
This can be set to either true/false to enable/disable or a custom greeting string.
|
||||
|
||||
<hr>
|
||||
|
||||
### `motd`
|
||||
#### Value: `boolean`
|
||||
#### Default: `true`
|
||||
The message of the day shows the current major.minor version and
|
||||
includes a small range of things added in the current release.
|
||||
|
||||
This can be set to `false` to disable the message.
|
||||
|
||||
<hr>
|
||||
|
||||
### `fuzzy`
|
||||
#### Value: `boolean`
|
||||
#### Default: `false`
|
||||
Toggles the functionality of fuzzy history searching, usable
|
||||
via the menu in Ctrl-R. Fuzzy searching is an approximate searching
|
||||
method, which means results that match *closest* will be shown instead
|
||||
of an exact match.
|
||||
|
||||
<hr>
|
||||
|
||||
### `notifyJobFinish`
|
||||
#### Value: `boolean`
|
||||
#### Default: `true`
|
||||
If this is enabled, when a background job is finished,
|
||||
a [notification](../notifications) will be sent.
|
||||
|
||||
### `processorSkipList`
|
||||
#### Value: `table`
|
||||
#### Default: `{}`
|
||||
A table listing the names of command processors to skip.
|
||||
53
docs/features/runner-mode.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
title: Runner Mode
|
||||
description: Customize the interactive script/command runner.
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Features"
|
||||
---
|
||||
|
||||
Hilbish allows you to change how interactive text can be interpreted.
|
||||
This is mainly due to the fact that the default method Hilbish uses
|
||||
is that it runs Lua first and then falls back to shell script.
|
||||
|
||||
In some cases, someone might want to switch to just shell script to avoid
|
||||
it while interactive but still have a Lua config, or go full Lua to use
|
||||
Hilbish as a REPL. This also allows users to add alternative languages like
|
||||
Fennel as the interactive script runner.
|
||||
|
||||
Runner mode can also be used to handle specific kinds of input before
|
||||
evaluating like normal, which is how [Link.hsh](https://github.com/TorchedSammy/Link.hsh)
|
||||
handles links.
|
||||
|
||||
The "runner mode" of Hilbish is customizable via `hilbish.runnerMode`,
|
||||
which determines how Hilbish will run user input. By default, this is
|
||||
set to `hybrid` which is the previously mentioned behaviour of running Lua
|
||||
first then going to shell script. If you want the reverse order, you can
|
||||
set it to `hybridRev` and for isolated modes there is `sh` and `lua`
|
||||
respectively.
|
||||
|
||||
You can also set it to a function, which will be called everytime Hilbish
|
||||
needs to run interactive input. For more detail, see the [API documentation](../../api/hilbish/hilbish.runner)
|
||||
|
||||
The `hilbish.runner` interface is an alternative to using `hilbish.runnerMode`
|
||||
and also provides the shell script and Lua runner functions that Hilbish itself uses.
|
||||
|
||||
## Functions
|
||||
These are the "low level" functions for the `hilbish.runner` interface.
|
||||
|
||||
+ setMode(mode) > The same as `hilbish.runnerMode`
|
||||
+ sh(input) -> table > Runs `input` in Hilbish's sh interpreter
|
||||
+ lua(input) -> table > Evals `input` as Lua code
|
||||
|
||||
These functions should be preferred over the previous ones.
|
||||
+ setCurrent(mode) > The same as `setMode`, but works with runners managed
|
||||
via the functions below.
|
||||
+ add(name, runner) > Adds a runner to a table of available runners. The `runner`
|
||||
argument is either a function or a table with a run callback.
|
||||
+ set(name, runner) > The same as `add` but requires passing a table and
|
||||
overwrites if the `name`d runner already exists.
|
||||
+ get(name) > runner > Gets a runner by name. It is a table with at least a
|
||||
run function, to run input.
|
||||
+ exec(cmd, runnerName) > Runs `cmd` with a runner. If `runnerName` isn't passed,
|
||||
the current runner mode is used.
|
||||
91
docs/getting-started.md
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Getting Started
|
||||
layout: doc
|
||||
weight: -10
|
||||
menu: docs
|
||||
---
|
||||
|
||||
To start Hilbish, open a terminal. If Hilbish has been installed and is not the
|
||||
default shell, you can simply run `hilbish` to start it. This will launch
|
||||
a normal interactive session.
|
||||
To exit, you can either run the `exit` command or hit Ctrl+D.
|
||||
|
||||
# Setting as Default
|
||||
## Login shell
|
||||
There are a few ways to make Hilbish your default shell. A simple way is
|
||||
to make it your user/login shell.
|
||||
|
||||
{{< warning `It is not recommended to set Hilbish as your login shell. That
|
||||
is expected to be a POSIX compliant shell, which Hilbish is not. Though if
|
||||
you still decide to do it, there will just be a few variables missing in
|
||||
your environment` >}}
|
||||
|
||||
To do that, simply run `chsh -s /usr/bin/hilbish`.
|
||||
Some distros (namely Fedora) might have `lchsh` instead, which is used like `lchsh <user>`.
|
||||
When prompted, you can put the path for Hilbish.
|
||||
|
||||
## Default with terminal
|
||||
The simpler way is to set the default shell for your terminal. The way of
|
||||
doing this depends on how your terminal settings are configured.
|
||||
|
||||
## Run after login shell
|
||||
Some shells (like zsh) have an rc file, like `.zlogin`, which is ran when the shell session
|
||||
is a login shell. In that file, you can run Hilbish. Example:
|
||||
|
||||
```
|
||||
exec hilbish -S -l
|
||||
```
|
||||
|
||||
This will replace the shell with Hilbish, set $SHELL to Hilbish and launch it as a login shell.
|
||||
|
||||
# Configuration
|
||||
Once installation and setup has been done, you can then configure Hilbish.
|
||||
It is configured and scripted via Lua, so the config file is a Lua file.
|
||||
You can use any pure Lua library to do whatever you want.
|
||||
|
||||
Hilbish's sample configuration is usually located in `hilbish.dataDir .. '/.hilbishrc.lua'`.
|
||||
You can print that path via Lua to see what it is: `print(hilbish.dataDir .. '/.hilbishrc.lua')`.
|
||||
As an example, it will usually will result in `/usr/share/hilbish/.hilbishrc.lua` on Linux.
|
||||
|
||||
To edit your user configuration, you can copy that file to `hilbish.userDir.config .. '/hilbish/init.lua'`,
|
||||
which follows XDG on Linux and MacOS, and is located in %APPDATA% on Windows.
|
||||
|
||||
As the directory is usually `~/.config` on Linux, you can run this command to copy it:
|
||||
`cp /usr/share/hilbish/.hilbishrc.lua ~/.config/hilbish/init.lua`
|
||||
|
||||
Now we can get to customization!
|
||||
|
||||
If we closely examine a small snippet of the default config:
|
||||
```lua
|
||||
-- Default Hilbish config
|
||||
-- .. with some omitted code .. --
|
||||
|
||||
local function doPrompt(fail)
|
||||
hilbish.prompt(lunacolors.format(
|
||||
'{blue}%u {cyan}%d ' .. (fail and '{red}' or '{green}') .. '∆ '
|
||||
))
|
||||
end
|
||||
|
||||
doPrompt()
|
||||
|
||||
bait.catch('command.exit', function(code)
|
||||
doPrompt(code ~= 0)
|
||||
end)
|
||||
```
|
||||
|
||||
We see a whopping **three** Hilbish libraries being used in this part of code.
|
||||
First is of course, named after the shell itself, [`hilbish`](../api/hilbish). This is kind of a
|
||||
"catch-all" namespace for functions that directly related to shell functionality/settings.
|
||||
|
||||
And as we can see, the [hilbish.prompt](../api/hilbish/#prompt) function is used
|
||||
to change our prompt. Change our prompt to what, exactly?
|
||||
|
||||
The doc for the function states that the verbs `%u` and `%d`are used for username and current directory
|
||||
of the shell, respectively.
|
||||
|
||||
We wrap this in the [`lunacolors.format`](../lunacolors) function, to give
|
||||
our prompt some nice color.
|
||||
|
||||
But you might have also noticed that this is in the `doPrompt` function, which is called once,
|
||||
and then used again in a [bait](../api/bait) hook. Specifically, the `command.exit` hook,
|
||||
which is called after a command exits, so when it finishes running.
|
||||
11
docs/hooks/_index.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Signals
|
||||
description:
|
||||
layout: doc
|
||||
weight: -50
|
||||
menu:
|
||||
docs
|
||||
---
|
||||
|
||||
Signals are global events emitted with the [Bait](../api/bait) module.
|
||||
For more detail on how to use these signals, you may check the Bait page.
|
||||
67
docs/hooks/command.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Command
|
||||
description:
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Signals"
|
||||
---
|
||||
|
||||
## command.preexec
|
||||
Thrown right before a command is executed.
|
||||
|
||||
#### Variables
|
||||
`string` **`input`**
|
||||
The raw string that the user typed. This will include the text
|
||||
without changes applied to it (argument substitution, alias expansion,
|
||||
etc.)
|
||||
|
||||
`string` **`cmdStr`**
|
||||
The command that will be directly executed by the current runner.
|
||||
|
||||
<hr>
|
||||
|
||||
## command.exit
|
||||
Thrown after the user's ran command is finished.
|
||||
|
||||
#### Variables
|
||||
`number` **`code`**
|
||||
The exit code of what was executed.
|
||||
|
||||
`string` **`cmdStr`**
|
||||
The command or code that was executed
|
||||
|
||||
<hr>
|
||||
|
||||
## command.not-found
|
||||
Thrown if the command attempted to execute was not found.
|
||||
This can be used to customize the text printed when a command is not found.
|
||||
Example:
|
||||
```lua
|
||||
local bait = require 'bait'
|
||||
-- Remove any present handlers on `command.not-found`
|
||||
|
||||
local notFoundHooks = bait.hooks 'command.not-found'
|
||||
for _, hook in ipairs(notFoundHooks) do
|
||||
bait.release('command.not-found', hook)
|
||||
end
|
||||
|
||||
-- then assign custom
|
||||
bait.catch('command.not-found', function(cmd)
|
||||
print(string.format('The command "%s" was not found.', cmd))
|
||||
end)
|
||||
```
|
||||
|
||||
#### Variables
|
||||
`string` **`cmdStr`**
|
||||
The name of the command.
|
||||
|
||||
<hr>
|
||||
|
||||
## command.not-executable
|
||||
Thrown when the user attempts to run a file that is not executable
|
||||
(like a text file, or Unix binary without +x permission).
|
||||
|
||||
#### Variables
|
||||
`string` **`cmdStr`**
|
||||
The name of the command.
|
||||
71
docs/hooks/hilbish.md
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Hilbish
|
||||
description:
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Signals"
|
||||
---
|
||||
|
||||
## hilbish.exit
|
||||
Sent when Hilbish is going to exit.
|
||||
|
||||
#### Variables
|
||||
This signal returns no variables.
|
||||
|
||||
<hr>
|
||||
|
||||
## hilbish.vimMode
|
||||
Sent when the Vim mode of Hilbish is changed (like from insert to normal mode).
|
||||
This can be used to change the prompt and notify based on Vim mode.
|
||||
|
||||
#### Variables
|
||||
`string` **`modeName`**
|
||||
The mode that has been set.
|
||||
Can be these values: `insert`, `normal`, `delete` or `replace`
|
||||
|
||||
<hr>
|
||||
|
||||
## hilbish.cancel
|
||||
Sent when the user cancels their command input with Ctrl-C
|
||||
|
||||
#### Variables
|
||||
This signal returns no variables.
|
||||
|
||||
<hr>
|
||||
|
||||
## hilbish.notification
|
||||
Thrown when a [notification](../../features/notifications) is sent.
|
||||
|
||||
#### Variables
|
||||
`table` **`notification`**
|
||||
The notification. The properties are defined in the link above.
|
||||
|
||||
<hr>
|
||||
|
||||
## hilbish.cd
|
||||
Sent when the current directory of the shell is changed (via interactive means.)
|
||||
If you are implementing a custom command that changes the directory of the shell,
|
||||
you must throw this hook manually for correctness.
|
||||
|
||||
#### Variables
|
||||
`string` **`path`**
|
||||
Absolute path of the directory that was changed to.
|
||||
|
||||
`string` **`oldPath`**
|
||||
Absolute path of the directory Hilbish *was* in.
|
||||
|
||||
<hr>
|
||||
|
||||
## hilbish.vimAction
|
||||
Sent when the user does a "vim action," being something like yanking or pasting text.
|
||||
See `doc vim-mode actions` for more info.
|
||||
|
||||
#### Variables
|
||||
`string` **`actionName`**
|
||||
Absolute path of the directory that was changed to.
|
||||
|
||||
`table` **`args`**
|
||||
Table of args relating to the Vim action.
|
||||
|
||||
<hr>
|
||||
7
docs/hooks/job.md
Normal file
@ -0,0 +1,7 @@
|
||||
Note: `job` refers to a job object. You can check `doc jobs` for more
|
||||
detail.
|
||||
|
||||
+ `job.start` -> job > Thrown when a new background job starts.
|
||||
|
||||
+ `job.done` -> job > Thrown when a background jobs exits.
|
||||
|
||||
40
docs/hooks/signal.md
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Signal
|
||||
description:
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Signals"
|
||||
---
|
||||
|
||||
## signal.sigint
|
||||
Thrown when Hilbish receive the SIGINT signal,
|
||||
aka when Ctrl-C is pressed.
|
||||
|
||||
#### Variables
|
||||
This signal returns no variables.
|
||||
|
||||
<hr>
|
||||
|
||||
## signal.resize
|
||||
Thrown when the terminal is resized.
|
||||
|
||||
#### Variables
|
||||
This signal returns no variables.
|
||||
|
||||
<hr>
|
||||
|
||||
## signal.sigusr1
|
||||
Thrown when SIGUSR1 is sent to Hilbish.
|
||||
|
||||
#### Variables
|
||||
This signal returns no variables.
|
||||
|
||||
<hr>
|
||||
|
||||
## signal.sigusr2
|
||||
Thrown when SIGUSR2 is sent to Hilbish.
|
||||
|
||||
#### Variables
|
||||
This signal returns no variables.
|
||||
|
||||
48
docs/jobs.md
Normal file
@ -0,0 +1,48 @@
|
||||
(This has mainly been replaced by [hilbish.jobs](../api/hilbish.jobs)).
|
||||
|
||||
Hilbish has pretty standard job control. It's missing one or two things,
|
||||
but works well. One thing which is different from other shells
|
||||
(besides Hilbish) itself is the API for jobs, and of course it's in Lua.
|
||||
You can add jobs, stop and delete (disown) them and even get output.
|
||||
|
||||
# Job Interface
|
||||
The job interface refers to `hilbish.jobs`.
|
||||
## Functions
|
||||
(Note that in the list here, they're called from `hilbish.jobs`, so
|
||||
a listing of `foo` would mean `hilbish.jobs.foo`)
|
||||
|
||||
- `all()` -> {jobs}: Returns a table of all jobs.
|
||||
- `last()` -> job: Returns the last added job.
|
||||
- `get(id)` -> job: Get a job by its ID.
|
||||
- `add(cmdstr, args, execPath)` -> job: Adds a new job to the job table.
|
||||
Note that this does not run the command; You have to start it manually.
|
||||
`cmdstr` is the user's input for the job, `args` is a table of arguments
|
||||
for the command. It includes arg0 (don't set it as entry 0 in the table)
|
||||
and `execPath` is an absolute path for the command executable.
|
||||
- `disown(id)`: Removes a job by ID from the job table.
|
||||
|
||||
# Job Object
|
||||
A job object is a piece of `userdata`. All the functions of a job require
|
||||
you to call them with a colon, since they are *methods* for the job object.
|
||||
Example: hilbish.jobs.last():foreground()
|
||||
Which will foreground the last job.
|
||||
|
||||
You can still have a job object for a disowned job,
|
||||
it just won't be *working* anywhere. :^)
|
||||
|
||||
## Properties
|
||||
- `cmd`: command string
|
||||
- `running`: boolean whether the job is running
|
||||
- `id`: unique id for the job
|
||||
- `pid`: process id for the job
|
||||
- `exitCode`: exit code of the job
|
||||
In ordinary cases you'd prefer to use the `id` instead of `pid`.
|
||||
The `id` is unique to Hilbish and is how you get jobs with the
|
||||
`hilbish.jobs` interface. It may also not describe the job entirely.
|
||||
|
||||
## Functions
|
||||
- `stop()`: Stops the job.
|
||||
- `start()`: Starts the job.
|
||||
- `foreground()`: Set the job as the current running foreground process, or
|
||||
run it in the foreground after it has been suspended.
|
||||
- `background()`: Run the job in the background after it has been suspended.
|
||||
44
docs/lunacolors.md
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Lunacolors
|
||||
layout: doc
|
||||
weight: -60
|
||||
menu: docs
|
||||
---
|
||||
|
||||
Lunacolors is an ANSI color/styling library for Lua. It is included
|
||||
by default in standard Hilbish distributions to provide easy styling
|
||||
for things like prompts and text.
|
||||
|
||||
For simple usage, a single color or style is enough. For example,
|
||||
you can just use `lunacolors.blue 'Hello world'` and that'll return
|
||||
blue text which you can print. This includes styles like bold,
|
||||
underline, etc.
|
||||
|
||||
In other usage, you may want to use a format string instead of having
|
||||
multiple nested functions for different styles. This is where the format
|
||||
function comes in. You can used named keywords to style a section of text.
|
||||
|
||||
The list of arguments are:
|
||||
Colors:
|
||||
- black
|
||||
- red
|
||||
- green
|
||||
- yellow
|
||||
- blue
|
||||
- magenta
|
||||
- cyan
|
||||
- white
|
||||
|
||||
Styles:
|
||||
- reset
|
||||
- bold
|
||||
- dim
|
||||
- italic
|
||||
- underline
|
||||
- invert
|
||||
|
||||
For the colors, there are background and bright variants. Background color
|
||||
variants have a `Bg` suffix, while bright variants use the `bright` prefix.
|
||||
These can also be combined. Note that appropriate camel casing must be applied.
|
||||
For example, bright blue would be written as `brightBlue`, a cyan background as
|
||||
`cyanBg`, and combining them would result in `brightBlueBg`.
|
||||
19
docs/nature/_index.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Nature
|
||||
layout: doc
|
||||
weight: -90
|
||||
menu: docs
|
||||
---
|
||||
|
||||
A bit after creation, we have the outside nature. Little plants, seeds,
|
||||
growing to their final phase: a full plant. A lot of Hilbish itself is
|
||||
written in Go, but there are parts made in Lua, being most builtins
|
||||
(`doc`, `cd`, cdr), completions, and other things.
|
||||
|
||||
Hilbish's Lua core module is called `nature`.
|
||||
It runs after Hilbish's Go core does.
|
||||
|
||||
# Nature Modules
|
||||
Currently, `nature` provides 1 intended public module: `nature.dirs`.
|
||||
It is a simple API for managing recent directories and old
|
||||
current working directory.
|
||||
103
docs/nature/dirs.md
Normal file
@ -0,0 +1,103 @@
|
||||
---
|
||||
title: Module dirs
|
||||
description: internal directory management
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Nature"
|
||||
---
|
||||
|
||||
|
||||
## Introduction
|
||||
The dirs module defines a small set of functions to store and manage
|
||||
directories.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#setOld">setOld(d)</a>|Sets the old directory string.|
|
||||
|<a href="#recent">recent(idx)</a>|Get entry from recent directories list based on index.|
|
||||
|<a href="#push">push(dir)</a>|Add `dir` to the recent directories list.|
|
||||
|<a href="#pop">pop(num)</a>|Remove the specified amount of dirs from the recent directories list.|
|
||||
|<a href="#peak">peak(num)</a>|Look at `num` amount of recent directories, starting from the latest.|
|
||||
<hr>
|
||||
<div id='peak'>
|
||||
<h4 class='heading'>
|
||||
dirs.peak(num)
|
||||
<a href="#peak" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Look at `num` amount of recent directories, starting from the latest.
|
||||
This returns a table of recent directories, up to the `num` amount.
|
||||
#### Parameters
|
||||
`num` **`number`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='pop'>
|
||||
<h4 class='heading'>
|
||||
dirs.pop(num)
|
||||
<a href="#pop" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Remove the specified amount of dirs from the recent directories list.
|
||||
#### Parameters
|
||||
`num` **`number`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='push'>
|
||||
<h4 class='heading'>
|
||||
dirs.push(dir)
|
||||
<a href="#push" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Add `dir` to the recent directories list.
|
||||
#### Parameters
|
||||
`dir` **`string`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='recent'>
|
||||
<h4 class='heading'>
|
||||
dirs.recent(idx)
|
||||
<a href="#recent" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Get entry from recent directories list based on index.
|
||||
#### Parameters
|
||||
`idx` **`number`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='setOld'>
|
||||
<h4 class='heading'>
|
||||
dirs.setOld(d)
|
||||
<a href="#setOld" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Sets the old directory string.
|
||||
#### Parameters
|
||||
`d` **`string`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
76
docs/nature/doc.md
Normal file
@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Module doc
|
||||
description: command-line doc rendering
|
||||
layout: doc
|
||||
menu:
|
||||
docs:
|
||||
parent: "Nature"
|
||||
---
|
||||
|
||||
|
||||
## Introduction
|
||||
The doc module contains a small set of functions
|
||||
used by the Greenhouse pager to render parts of the documentation pages.
|
||||
This is only documented for the sake of it. It's only intended use
|
||||
is by the Greenhouse pager.
|
||||
|
||||
## Functions
|
||||
|||
|
||||
|----|----|
|
||||
|<a href="#renderInfoBlock">renderInfoBlock(type, text)</a>|Renders an info block. An info block is a block of text with|
|
||||
|<a href="#renderCodeBlock">renderCodeBlock(text)</a>|Assembles and renders a code block. This returns|
|
||||
|<a href="#highlight">highlight(text)</a>|Performs basic Lua code highlighting.|
|
||||
<hr>
|
||||
<div id='highlight'>
|
||||
<h4 class='heading'>
|
||||
doc.highlight(text)
|
||||
<a href="#highlight" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Performs basic Lua code highlighting.
|
||||
#### Parameters
|
||||
`text` **`string`**
|
||||
Code/text to do highlighting on.
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='renderCodeBlock'>
|
||||
<h4 class='heading'>
|
||||
doc.renderCodeBlock(text)
|
||||
<a href="#renderCodeBlock" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Assembles and renders a code block. This returns
|
||||
the supplied text based on the number of command line columns,
|
||||
and styles it to resemble a code block.
|
||||
#### Parameters
|
||||
`text` **`string`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id='renderInfoBlock'>
|
||||
<h4 class='heading'>
|
||||
doc.renderInfoBlock(type, text)
|
||||
<a href="#renderInfoBlock" class='heading-link'>
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
Renders an info block. An info block is a block of text with
|
||||
an icon and styled text block.
|
||||
#### Parameters
|
||||
`type` **`string`**
|
||||
Type of info block. The only one specially styled is the `warning`.
|
||||
|
||||
`text` **`string`**
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
1
docs/timers.md
Normal file
@ -0,0 +1 @@
|
||||
This has been moved to the `hilbish.timers` API doc (accessible by `doc api hilbish.timers`)
|
||||
11
docs/vim-mode/_index.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Vim Mode
|
||||
layout: doc
|
||||
weight: -90
|
||||
menu: docs
|
||||
---
|
||||
|
||||
Hilbish has a Vim binding input mode accessible for use.
|
||||
It can be enabled with the `hilbish.inputMode` function (check `doc hilbish`).
|
||||
|
||||
This is documentation for everything relating to it.
|
||||
25
docs/vim-mode/actions.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: Actions
|
||||
layout: doc
|
||||
weight: -80
|
||||
menu:
|
||||
docs:
|
||||
parent: "Vim Mode"
|
||||
---
|
||||
|
||||
Vim actions are essentially just when a user uses a Vim keybind.
|
||||
Things like yanking and pasting are Vim actions.
|
||||
This is not an "offical Vim thing," just a Hilbish thing.
|
||||
|
||||
The `hilbish.vimAction` hook is thrown whenever a Vim action occurs.
|
||||
It passes 2 arguments: the action name, and an array (table) of args
|
||||
relating to it.
|
||||
|
||||
Here is documentation for what the table of args will hold for an
|
||||
appropriate Vim action.
|
||||
|
||||
- `yank`: register, yankedText
|
||||
The first argument for the yank action is the register yankedText goes to.
|
||||
|
||||
- `paste`: register, pastedText
|
||||
The first argument for the paste action is the register pastedText is taken from.
|
||||
28
emmyLuaDocs/bait.lua
Normal file
@ -0,0 +1,28 @@
|
||||
--- @meta
|
||||
|
||||
local bait = {}
|
||||
|
||||
--- Catches an event. This function can be used to act on events.
|
||||
---
|
||||
---
|
||||
function bait.catch(name, cb) end
|
||||
|
||||
--- Catches an event, but only once. This will remove the hook immediately after it runs for the first time.
|
||||
function bait.catchOnce(name, cb) end
|
||||
|
||||
--- Returns a table of functions that are hooked on an event with the corresponding `name`.
|
||||
function bait.hooks(name) end
|
||||
|
||||
--- Removes the `catcher` for the event with `name`.
|
||||
--- For this to work, `catcher` has to be the same function used to catch
|
||||
--- an event, like one saved to a variable.
|
||||
---
|
||||
---
|
||||
function bait.release(name, catcher) end
|
||||
|
||||
--- Throws a hook with `name` with the provided `args`.
|
||||
---
|
||||
---
|
||||
function bait.throw(name, ...args) end
|
||||
|
||||
return bait
|
||||
18
emmyLuaDocs/commander.lua
Normal file
@ -0,0 +1,18 @@
|
||||
--- @meta
|
||||
|
||||
local commander = {}
|
||||
|
||||
--- Removes the named command. Note that this will only remove Commander-registered commands.
|
||||
function commander.deregister(name) end
|
||||
|
||||
--- Adds a new command with the given `name`. When Hilbish has to run a command with a name,
|
||||
--- it will run the function providing the arguments and sinks.
|
||||
---
|
||||
---
|
||||
function commander.register(name, cb) end
|
||||
|
||||
--- Returns all registered commanders. Returns a list of tables with the following keys:
|
||||
--- - `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||
function commander.registry() end
|
||||
|
||||
return commander
|
||||
54
emmyLuaDocs/fs.lua
Normal file
@ -0,0 +1,54 @@
|
||||
--- @meta
|
||||
|
||||
local fs = {}
|
||||
|
||||
--- Returns an absolute version of the `path`.
|
||||
--- This can be used to resolve short paths like `..` to `/home/user`.
|
||||
function fs.abs(path) end
|
||||
|
||||
--- Returns the "basename," or the last part of the provided `path`. If path is empty,
|
||||
--- `.` will be returned.
|
||||
function fs.basename(path) end
|
||||
|
||||
--- Changes Hilbish's directory to `dir`.
|
||||
function fs.cd(dir) end
|
||||
|
||||
--- Returns the directory part of `path`. If a file path like
|
||||
--- `~/Documents/doc.txt` then this function will return `~/Documents`.
|
||||
function fs.dir(path) end
|
||||
|
||||
--- Match all files based on the provided `pattern`.
|
||||
--- For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match
|
||||
---
|
||||
---
|
||||
function fs.glob(pattern) end
|
||||
|
||||
--- Takes any list of paths and joins them based on the operating system's path separator.
|
||||
---
|
||||
---
|
||||
function fs.join(...path) end
|
||||
|
||||
--- Creates a new directory with the provided `name`.
|
||||
--- With `recursive`, mkdir will create parent directories.
|
||||
---
|
||||
---
|
||||
function fs.mkdir(name, recursive) end
|
||||
|
||||
--- Returns a pair of connected files, also known as a pipe.
|
||||
--- The type returned is a Lua file, same as returned from `io` functions.
|
||||
function fs.fpipe() end
|
||||
|
||||
--- Returns a list of all files and directories in the provided path.
|
||||
function fs.readdir(path) end
|
||||
|
||||
--- Returns the information about a given `path`.
|
||||
--- The returned table contains the following values:
|
||||
--- name (string) - Name of the path
|
||||
--- size (number) - Size of the path in bytes
|
||||
--- mode (string) - Unix permission mode in an octal format string (with leading 0)
|
||||
--- isDir (boolean) - If the path is a directory
|
||||
---
|
||||
---
|
||||
function fs.stat(path) end
|
||||
|
||||
return fs
|
||||
222
emmyLuaDocs/hilbish.lua
Normal file
@ -0,0 +1,222 @@
|
||||
--- @meta
|
||||
|
||||
local hilbish = {}
|
||||
|
||||
--- This is an alias (ha) for the [hilbish.alias](../#alias) function.
|
||||
--- @param alias string
|
||||
--- @param cmd string
|
||||
function hilbish.aliases.add(alias, cmd) end
|
||||
|
||||
--- Return binaries/executables based on the provided parameters.
|
||||
--- This function is meant to be used as a helper in a command completion handler.
|
||||
---
|
||||
---
|
||||
function hilbish.completion.bins(query, ctx, fields) end
|
||||
|
||||
--- Calls a completer function. This is mainly used to call a command completer, which will have a `name`
|
||||
--- in the form of `command.name`, example: `command.git`.
|
||||
--- You can check the Completions doc or `doc completions` for info on the `completionGroups` return value.
|
||||
function hilbish.completion.call(name, query, ctx, fields) end
|
||||
|
||||
--- Returns file matches based on the provided parameters.
|
||||
--- This function is meant to be used as a helper in a command completion handler.
|
||||
function hilbish.completion.files(query, ctx, fields) end
|
||||
|
||||
--- This function contains the general completion handler for Hilbish. This function handles
|
||||
--- completion of everything, which includes calling other command handlers, binaries, and files.
|
||||
--- This function can be overriden to supply a custom handler. Note that alias resolution is required to be done in this function.
|
||||
---
|
||||
---
|
||||
function hilbish.completion.handler(line, pos) end
|
||||
|
||||
--- Sets an alias, with a name of `cmd` to another command.
|
||||
---
|
||||
---
|
||||
function hilbish.alias(cmd, orig) end
|
||||
|
||||
--- Appends the provided dir to the command path (`$PATH`)
|
||||
---
|
||||
---
|
||||
function hilbish.appendPath(dir) end
|
||||
|
||||
--- Registers a completion handler for the specified scope.
|
||||
--- A `scope` is expected to be `command.<cmd>`,
|
||||
--- replacing <cmd> with the name of the command (for example `command.git`).
|
||||
--- The documentation for completions, under Features/Completions or `doc completions`
|
||||
--- provides more details.
|
||||
---
|
||||
---
|
||||
function hilbish.complete(scope, cb) end
|
||||
|
||||
--- Returns the current directory of the shell.
|
||||
function hilbish.cwd() end
|
||||
|
||||
--- Replaces the currently running Hilbish instance with the supplied command.
|
||||
--- This can be used to do an in-place restart.
|
||||
function hilbish.exec(cmd) end
|
||||
|
||||
--- Puts `fn` in a Goroutine.
|
||||
--- This can be used to run any function in another thread at the same time as other Lua code.
|
||||
--- **NOTE: THIS FUNCTION MAY CRASH HILBISH IF OUTSIDE VARIABLES ARE ACCESSED.**
|
||||
--- **This is a limitation of the Lua runtime.**
|
||||
function hilbish.goro(fn) end
|
||||
|
||||
--- Line highlighter handler.
|
||||
--- This is mainly for syntax highlighting, but in reality could set the input
|
||||
--- of the prompt to *display* anything. The callback is passed the current line
|
||||
--- and is expected to return a line that will be used as the input display.
|
||||
--- Note that to set a highlighter, one has to override this function.
|
||||
---
|
||||
function hilbish.highlighter(line) end
|
||||
|
||||
--- The command line hint handler. It gets called on every key insert to
|
||||
--- determine what text to use as an inline hint. It is passed the current
|
||||
--- line and cursor position. It is expected to return a string which is used
|
||||
--- as the text for the hint. This is by default a shim. To set hints,
|
||||
--- override this function with your custom handler.
|
||||
---
|
||||
---
|
||||
function hilbish.hinter(line, pos) end
|
||||
|
||||
--- Sets the input mode for Hilbish's line reader.
|
||||
--- `emacs` is the default. Setting it to `vim` changes behavior of input to be
|
||||
--- Vim-like with modes and Vim keybinds.
|
||||
function hilbish.inputMode(mode) end
|
||||
|
||||
--- Runs the `cb` function every specified amount of `time`.
|
||||
--- This creates a timer that ticking immediately.
|
||||
function hilbish.interval(cb, time) end
|
||||
|
||||
--- Changes the text prompt when Hilbish asks for more input.
|
||||
--- This will show up when text is incomplete, like a missing quote
|
||||
---
|
||||
---
|
||||
function hilbish.multiprompt(str) end
|
||||
|
||||
--- Prepends `dir` to $PATH.
|
||||
function hilbish.prependPath(dir) end
|
||||
|
||||
--- Changes the shell prompt to the provided string.
|
||||
--- There are a few verbs that can be used in the prompt text.
|
||||
--- These will be formatted and replaced with the appropriate values.
|
||||
--- `%d` - Current working directory
|
||||
--- `%u` - Name of current user
|
||||
--- `%h` - Hostname of device
|
||||
---
|
||||
function hilbish.prompt(str, typ) end
|
||||
|
||||
--- Read input from the user, using Hilbish's line editor/input reader.
|
||||
--- This is a separate instance from the one Hilbish actually uses.
|
||||
--- Returns `input`, will be nil if Ctrl-D is pressed, or an error occurs.
|
||||
function hilbish.read(prompt) end
|
||||
|
||||
--- Executed the `cb` function after a period of `time`.
|
||||
--- This creates a Timer that starts ticking immediately.
|
||||
function hilbish.timeout(cb, time) end
|
||||
|
||||
--- Checks if `name` is a valid command.
|
||||
--- Will return the path of the binary, or a basename if it's a commander.
|
||||
function hilbish.which(name) end
|
||||
|
||||
--- Puts a job in the background. This acts the same as initially running a job.
|
||||
function hilbish.jobs:background() end
|
||||
|
||||
--- Puts a job in the foreground. This will cause it to run like it was
|
||||
--- executed normally and wait for it to complete.
|
||||
function hilbish.jobs:foreground() end
|
||||
|
||||
--- Evaluates `cmd` as Lua input. This is the same as using `dofile`
|
||||
--- or `load`, but is appropriated for the runner interface.
|
||||
function hilbish.runner.lua(cmd) end
|
||||
|
||||
--- Starts running the job.
|
||||
function hilbish.jobs:start() end
|
||||
|
||||
--- Stops the job from running.
|
||||
function hilbish.jobs:stop() end
|
||||
|
||||
--- Loads a module at the designated `path`.
|
||||
--- It will throw if any error occurs.
|
||||
function hilbish.module.load(path) end
|
||||
|
||||
--- Starts a timer.
|
||||
function hilbish.timers:start() end
|
||||
|
||||
--- Stops a timer.
|
||||
function hilbish.timers:stop() end
|
||||
|
||||
--- Removes an alias.
|
||||
function hilbish.aliases.delete(name) end
|
||||
|
||||
--- Get a table of all aliases, with string keys as the alias and the value as the command.
|
||||
---
|
||||
---
|
||||
function hilbish.aliases.list() end
|
||||
|
||||
--- Resolves an alias to its original command. Will thrown an error if the alias doesn't exist.
|
||||
function hilbish.aliases.resolve(alias) end
|
||||
|
||||
--- Creates a new job. This function does not run the job. This function is intended to be
|
||||
--- used by runners, but can also be used to create jobs via Lua. Commanders cannot be ran as jobs.
|
||||
---
|
||||
---
|
||||
function hilbish.jobs.add(cmdstr, args, execPath) end
|
||||
|
||||
--- Returns a table of all job objects.
|
||||
function hilbish.jobs.all() end
|
||||
|
||||
--- Disowns a job. This simply deletes it from the list of jobs without stopping it.
|
||||
function hilbish.jobs.disown(id) end
|
||||
|
||||
--- Get a job object via its ID.
|
||||
--- @param id number
|
||||
--- @returns Job
|
||||
function hilbish.jobs.get(id) end
|
||||
|
||||
--- Returns the last added job to the table.
|
||||
function hilbish.jobs.last() end
|
||||
|
||||
--- Adds a command to the history.
|
||||
function hilbish.history.add(cmd) end
|
||||
|
||||
--- Retrieves all history as a table.
|
||||
function hilbish.history.all() end
|
||||
|
||||
--- Deletes all commands from the history.
|
||||
function hilbish.history.clear() end
|
||||
|
||||
--- Retrieves a command from the history based on the `index`.
|
||||
function hilbish.history.get(index) end
|
||||
|
||||
--- Returns the amount of commands in the history.
|
||||
function hilbish.history.size() end
|
||||
|
||||
--- Creates a timer that runs based on the specified `time`.
|
||||
function hilbish.timers.create(type, time, callback) end
|
||||
|
||||
--- Retrieves a timer via its ID.
|
||||
function hilbish.timers.get(id) end
|
||||
|
||||
--- Sets/toggles the option of automatically flushing output.
|
||||
--- A call with no argument will toggle the value.
|
||||
--- @param auto boolean|nil
|
||||
function hilbish:autoFlush(auto) end
|
||||
|
||||
--- Flush writes all buffered input to the sink.
|
||||
function hilbish:flush() end
|
||||
|
||||
--- Reads a liine of input from the sink.
|
||||
--- @returns string
|
||||
function hilbish:read() end
|
||||
|
||||
--- Reads all input from the sink.
|
||||
--- @returns string
|
||||
function hilbish:readAll() end
|
||||
|
||||
--- Writes data to a sink.
|
||||
function hilbish:write(str) end
|
||||
|
||||
--- Writes data to a sink with a newline at the end.
|
||||
function hilbish:writeln(str) end
|
||||
|
||||
return hilbish
|
||||
32
emmyLuaDocs/readline.lua
Normal file
@ -0,0 +1,32 @@
|
||||
--- @meta
|
||||
|
||||
local readline = {}
|
||||
|
||||
--- Deletes characters in the line by the given amount.
|
||||
function readline:deleteByAmount(amount) end
|
||||
|
||||
--- Returns the current input line.
|
||||
function readline:getLine() end
|
||||
|
||||
--- Returns the text that is at the register.
|
||||
function readline:getVimRegister(register) end
|
||||
|
||||
--- Inserts text into the Hilbish command line.
|
||||
function readline:insert(text) end
|
||||
|
||||
--- Prints a message *before* the prompt without it being interrupted by user input.
|
||||
function readline:log(text) end
|
||||
|
||||
--- Creates a new readline instance.
|
||||
function readline.new() end
|
||||
|
||||
--- Reads input from the user.
|
||||
function readline:read() end
|
||||
|
||||
--- Reads a keystroke from the user. This is in a format of something like Ctrl-L.
|
||||
function readline:getChar() end
|
||||
|
||||
--- Sets the vim register at `register` to hold the passed text.
|
||||
function readline:setVimRegister(register, text) end
|
||||
|
||||
return readline
|
||||
16
emmyLuaDocs/snail.lua
Normal file
@ -0,0 +1,16 @@
|
||||
--- @meta
|
||||
|
||||
local snail = {}
|
||||
|
||||
--- Changes the directory of the snail instance.
|
||||
--- The interpreter keeps its set directory even when the Hilbish process changes
|
||||
--- directory, so this should be called on the `hilbish.cd` hook.
|
||||
function snail:dir(path) end
|
||||
|
||||
--- Creates a new Snail instance.
|
||||
function snail.new() end
|
||||
|
||||
--- Runs a shell command. Works the same as `hilbish.run`, but only accepts a table of streams.
|
||||
function snail:run(command, streams) end
|
||||
|
||||
return snail
|
||||
18
emmyLuaDocs/terminal.lua
Normal file
@ -0,0 +1,18 @@
|
||||
--- @meta
|
||||
|
||||
local terminal = {}
|
||||
|
||||
--- Restores the last saved state of the terminal
|
||||
function terminal.restoreState() end
|
||||
|
||||
--- Saves the current state of the terminal.
|
||||
function terminal.saveState() end
|
||||
|
||||
--- Puts the terminal into raw mode.
|
||||
function terminal.setRaw() end
|
||||
|
||||
--- Gets the dimensions of the terminal. Returns a table with `width` and `height`
|
||||
--- NOTE: The size refers to the amount of columns and rows of text that can fit in the terminal.
|
||||
function terminal.size() end
|
||||
|
||||
return terminal
|
||||
83
emmyLuaDocs/util.lua
Normal file
@ -0,0 +1,83 @@
|
||||
--- @meta
|
||||
|
||||
local util = {}
|
||||
|
||||
---
|
||||
function util.AbbrevHome changes the user's home directory in the path string to ~ (tilde) end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
---
|
||||
function util.DoFile runs the contents of the file in the Lua runtime. end
|
||||
|
||||
---
|
||||
function util.DoString runs the code string in the Lua runtime. end
|
||||
|
||||
--- directory.
|
||||
function util.ExpandHome expands ~ (tilde) in the path, changing it to the user home end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
---
|
||||
function util.ForEach loops through a Lua table. end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
--- a string and a closure.
|
||||
function util.HandleStrCallback handles function parameters for Go functions which take end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
---
|
||||
function util.SetExports puts the Lua function exports in the table. end
|
||||
|
||||
--- It is accessible via the __docProp metatable. It is a table of the names of the fields.
|
||||
function util.SetField sets a field in a table, adding docs for it. end
|
||||
|
||||
--- is one which has a metatable proxy to ensure no overrides happen to it.
|
||||
--- It sets the field in the table and sets the __docProp metatable on the
|
||||
--- user facing table.
|
||||
function util.SetFieldProtected sets a field in a protected table. A protected table end
|
||||
|
||||
--- Sets/toggles the option of automatically flushing output.
|
||||
--- A call with no argument will toggle the value.
|
||||
--- @param auto boolean|nil
|
||||
function util:autoFlush(auto) end
|
||||
|
||||
--- Flush writes all buffered input to the sink.
|
||||
function util:flush() end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
--- Reads a liine of input from the sink.
|
||||
--- @returns string
|
||||
function util:read() end
|
||||
|
||||
--- Reads all input from the sink.
|
||||
--- @returns string
|
||||
function util:readAll() end
|
||||
|
||||
--- Writes data to a sink.
|
||||
function util:write(str) end
|
||||
|
||||
--- Writes data to a sink with a newline at the end.
|
||||
function util:writeln(str) end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
---
|
||||
function util. end
|
||||
|
||||
return util
|
||||
9
emmyLuaDocs/yarn.lua
Normal file
@ -0,0 +1,9 @@
|
||||
--- @meta
|
||||
|
||||
local yarn = {}
|
||||
|
||||
--- Creates a new, fresh Yarn thread.
|
||||
--- `fun` is the function that will run in the thread.
|
||||
function yarn.thread(fun) end
|
||||
|
||||
return yarn
|
||||
85
exec.go
Normal file
@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
//"github.com/yuin/gopher-lua/parse"
|
||||
)
|
||||
|
||||
var errNotExec = errors.New("not executable")
|
||||
var errNotFound = errors.New("not found")
|
||||
var runnerMode rt.Value = rt.NilValue
|
||||
|
||||
func runInput(input string, priv bool) {
|
||||
running = true
|
||||
runnerRun := hshMod.Get(rt.StringValue("runner")).AsTable().Get(rt.StringValue("run"))
|
||||
_, err := rt.Call1(l.MainThread(), runnerRun, rt.StringValue(input), rt.BoolValue(priv))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func handleLua(input string) (string, uint8, error) {
|
||||
cmdString := aliases.Resolve(input)
|
||||
// First try to load input, essentially compiling to bytecode
|
||||
chunk, err := l.CompileAndLoadLuaChunk("", []byte(cmdString), rt.TableValue(l.GlobalEnv()))
|
||||
if err != nil && noexecute {
|
||||
fmt.Println(err)
|
||||
/* if lerr, ok := err.(*lua.ApiError); ok {
|
||||
if perr, ok := lerr.Cause.(*parse.Error); ok {
|
||||
print(perr.Pos.Line == parse.EOF)
|
||||
}
|
||||
}
|
||||
*/
|
||||
return cmdString, 125, err
|
||||
}
|
||||
// And if there's no syntax errors and -n isnt provided, run
|
||||
if !noexecute {
|
||||
if chunk != nil {
|
||||
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(chunk))
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
return cmdString, 0, nil
|
||||
}
|
||||
|
||||
return cmdString, 125, err
|
||||
}
|
||||
|
||||
func splitInput(input string) ([]string, string) {
|
||||
// end my suffering
|
||||
// TODO: refactor this garbage
|
||||
quoted := false
|
||||
cmdArgs := []string{}
|
||||
sb := &strings.Builder{}
|
||||
cmdstr := &strings.Builder{}
|
||||
|
||||
for _, r := range input {
|
||||
if r == '"' {
|
||||
// start quoted input
|
||||
// this determines if other runes are replaced
|
||||
quoted = !quoted
|
||||
// dont add back quotes
|
||||
//sb.WriteRune(r)
|
||||
} else if !quoted && r == '~' {
|
||||
// if not in quotes and ~ is found then make it $HOME
|
||||
sb.WriteString(os.Getenv("HOME"))
|
||||
} else if !quoted && r == ' ' {
|
||||
// if not quoted and there's a space then add to cmdargs
|
||||
cmdArgs = append(cmdArgs, sb.String())
|
||||
sb.Reset()
|
||||
} else {
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
cmdstr.WriteRune(r)
|
||||
}
|
||||
if sb.Len() > 0 {
|
||||
cmdArgs = append(cmdArgs, sb.String())
|
||||
}
|
||||
|
||||
return cmdArgs, cmdstr.String()
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 58 KiB |
BIN
gallery/tab.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 99 KiB |
43
go.mod
@ -1,18 +1,37 @@
|
||||
module hilbish
|
||||
|
||||
go 1.16
|
||||
go 1.21
|
||||
|
||||
toolchain go1.22.2
|
||||
|
||||
require (
|
||||
github.com/Rosettea/Hilbiline v0.0.0-20210603231612-80054dac3650 // indirect
|
||||
github.com/bobappleyard/readline v0.0.0-20150707195538-7e300e02d38e
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/arnodel/golua v0.0.0-20230215163904-e0b5347eaaa1
|
||||
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504
|
||||
github.com/blackfireio/osinfo v1.0.5
|
||||
github.com/maxlandon/readline v1.0.14
|
||||
github.com/pborman/getopt v1.1.0
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56
|
||||
layeh.com/gopher-luar v1.0.8
|
||||
mvdan.cc/sh/v3 v3.3.0
|
||||
github.com/sahilm/fuzzy v0.1.1
|
||||
golang.org/x/sys v0.22.0
|
||||
golang.org/x/term v0.22.0
|
||||
mvdan.cc/sh/v3 v3.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||
github.com/arnodel/strftime v0.1.6 // indirect
|
||||
github.com/evilsocket/islazy v1.11.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
|
||||
replace mvdan.cc/sh/v3 => github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240815163633-562273e09b73
|
||||
|
||||
replace github.com/maxlandon/readline => ./golibs/readline
|
||||
|
||||
replace layeh.com/gopher-luar => github.com/layeh/gopher-luar v1.0.10
|
||||
|
||||
replace github.com/arnodel/golua => github.com/Rosettea/golua v0.0.0-20241104031959-5551ea280f23
|
||||
|
||||
92
go.sum
@ -1,52 +1,46 @@
|
||||
github.com/Rosettea/Hilbiline v0.0.0-20210603231612-80054dac3650 h1:nzFJUdJU8UJ1DA8mSQp4eoBtQyOJyecekVWusjfQsqE=
|
||||
github.com/Rosettea/Hilbiline v0.0.0-20210603231612-80054dac3650/go.mod h1:/FFZ4cgR6TXXYaskRUxyLIYdfG0PS4BPtWjWRQms754=
|
||||
github.com/bobappleyard/readline v0.0.0-20150707195538-7e300e02d38e h1:4G8AYOOwZdDWOiJR6D6JXaFmj5BDS7c5D5PyqsG/+Hg=
|
||||
github.com/bobappleyard/readline v0.0.0-20150707195538-7e300e02d38e/go.mod h1:fmqtV+Wqx0uFYLN1F4VhjZdtT56Dr8c3yA7nALFsw/Q=
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/google/renameio v1.0.1-0.20210406141108-81588dbe0453/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/Rosettea/golua v0.0.0-20241104031959-5551ea280f23 h1:mUZnT0gmDEmTkqXsbnDbuJ3CNil7DCOMiCQYgjbKIdI=
|
||||
github.com/Rosettea/golua v0.0.0-20241104031959-5551ea280f23/go.mod h1:9jzpYPiU2is0HVGCiuIOBSXdergHUW44IEjmuN1UrIE=
|
||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240815163633-562273e09b73 h1:zTTUJqNnrF2qf4LgygN8Oae5Uxn6ewH0hA8jyTCHfXw=
|
||||
github.com/Rosettea/sh/v3 v3.4.0-0.dev.0.20240815163633-562273e09b73/go.mod h1:YZalN5H7WNQw3DGij6IvHsEhn5YMW7M2FCwG6gnfKy4=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/arnodel/strftime v0.1.6 h1:0hc0pUvk8KhEMXE+htyaOUV42zNcf/csIbjzEFCJqsw=
|
||||
github.com/arnodel/strftime v0.1.6/go.mod h1:5NbK5XqYK8QpRZpqKNt4OlxLtIB8cotkLk4KTKzJfWs=
|
||||
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504 h1:R1/AOzdMbopSliUTTEHvHbyNmnZ3YxY5GvdhTkpPsSY=
|
||||
github.com/atsushinee/go-markdown-generator v0.0.0-20191121114853-83f9e1f68504/go.mod h1:kHBCvAXJIatTX1pw6tLiOspjGc3MhUDRlog9yrCUS+k=
|
||||
github.com/blackfireio/osinfo v1.0.5 h1:6hlaWzfcpb87gRmznVf7wSdhysGqLRz9V/xuSdCEXrA=
|
||||
github.com/blackfireio/osinfo v1.0.5/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/evilsocket/islazy v1.11.0 h1:B5w6uuS6ki6iDG+aH/RFeoMb8ijQh/pGabewqp2UeJ0=
|
||||
github.com/evilsocket/islazy v1.11.0/go.mod h1:muYH4x5MB5YRdkxnrOtrXLIBX6LySj1uFIqys94LKdo=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
|
||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
|
||||
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
|
||||
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.7.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw=
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1 h1:lCnv+lfrU9FRPGf8NeRuWAAPjNnema5WtBinMgs1fD8=
|
||||
golang.org/x/sys v0.0.0-20210521203332-0cec03c779c1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
layeh.com/gopher-luar v1.0.8 h1:Uqws1Z6T0vK6pZ7ehNNurLLSFcz7+E0EOHVM4FNiMQs=
|
||||
layeh.com/gopher-luar v1.0.8/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk=
|
||||
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
||||
mvdan.cc/sh/v3 v3.3.0 h1:ujzElMnry63f4I5sjPFxzo6xia+gwsHZM0yyauuyZ6k=
|
||||
mvdan.cc/sh/v3 v3.3.0/go.mod h1:dh3avhLDhJJ/MJKzbak6FYn+DJKUWk7Fb6Dh5mGdv6Y=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
|
||||
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
|
||||
@ -1,35 +1,365 @@
|
||||
// the event emitter
|
||||
/*
|
||||
Bait is the event emitter for Hilbish. Much like Node.js and
|
||||
its `events` system, many actions in Hilbish emit events.
|
||||
Unlike Node.js, Hilbish events are global. So make sure to
|
||||
pick a unique name!
|
||||
|
||||
Usage of the Bait module consists of userstanding
|
||||
event-driven architecture, but it's pretty simple:
|
||||
If you want to act on a certain event, you can `catch` it.
|
||||
You can act on events via callback functions.
|
||||
|
||||
Examples of this are in the Hilbish default config!
|
||||
Consider this part of it:
|
||||
```lua
|
||||
bait.catch('command.exit', function(code)
|
||||
running = false
|
||||
doPrompt(code ~= 0)
|
||||
doNotifyPrompt()
|
||||
end)
|
||||
```
|
||||
|
||||
What this does is, whenever the `command.exit` event is thrown,
|
||||
this function will set the user prompt.
|
||||
*/
|
||||
package bait
|
||||
|
||||
import (
|
||||
"github.com/chuckpreslar/emission"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"layeh.com/gopher-luar"
|
||||
"errors"
|
||||
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/arnodel/golua/lib/packagelib"
|
||||
)
|
||||
|
||||
type Bait struct{
|
||||
Em *emission.Emitter
|
||||
type listenerType int
|
||||
const (
|
||||
goListener listenerType = iota
|
||||
luaListener
|
||||
)
|
||||
|
||||
// Recoverer is a function which is called when a panic occurs in an event.
|
||||
type Recoverer func(event string, handler *Listener, err interface{})
|
||||
|
||||
// Listener is a struct that holds the handler for an event.
|
||||
type Listener struct{
|
||||
typ listenerType
|
||||
once bool
|
||||
caller func(...interface{}) rt.Value
|
||||
luaCaller *rt.Closure
|
||||
}
|
||||
|
||||
func New() Bait {
|
||||
return Bait{
|
||||
Em: emission.NewEmitter(),
|
||||
type Bait struct{
|
||||
Loader packagelib.Loader
|
||||
recoverer Recoverer
|
||||
handlers map[string][]*Listener
|
||||
rtm *rt.Runtime
|
||||
}
|
||||
|
||||
// New creates a new Bait instance.
|
||||
func New(rtm *rt.Runtime) *Bait {
|
||||
b := &Bait{
|
||||
handlers: make(map[string][]*Listener),
|
||||
rtm: rtm,
|
||||
}
|
||||
b.Loader = packagelib.Loader{
|
||||
Load: b.loaderFunc,
|
||||
Name: "bait",
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Emit throws an event.
|
||||
func (b *Bait) Emit(event string, args ...interface{}) []rt.Value {
|
||||
var returns []rt.Value
|
||||
handles := b.handlers[event]
|
||||
if handles == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for idx, handle := range handles {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
b.callRecoverer(event, handle, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if handle.typ == luaListener {
|
||||
funcVal := rt.FunctionValue(handle.luaCaller)
|
||||
var luaArgs []rt.Value
|
||||
for _, arg := range args {
|
||||
var luarg rt.Value
|
||||
switch arg.(type) {
|
||||
case rt.Value: luarg = arg.(rt.Value)
|
||||
default: luarg = rt.AsValue(arg)
|
||||
}
|
||||
luaArgs = append(luaArgs, luarg)
|
||||
}
|
||||
luaRet, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
|
||||
if err != nil {
|
||||
if event != "error" {
|
||||
b.Emit("error", event, handle.luaCaller, err.Error())
|
||||
return nil
|
||||
}
|
||||
// if there is an error in an error event handler, panic instead
|
||||
// (calls the go recoverer function)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if luaRet != rt.NilValue {
|
||||
returns = append(returns, luaRet)
|
||||
}
|
||||
} else {
|
||||
ret := handle.caller(args...)
|
||||
if ret != rt.NilValue {
|
||||
returns = append(returns, ret)
|
||||
}
|
||||
}
|
||||
|
||||
if handle.once {
|
||||
b.removeListener(event, idx)
|
||||
}
|
||||
}
|
||||
|
||||
return returns
|
||||
}
|
||||
|
||||
// On adds a Go function handler for an event.
|
||||
func (b *Bait) On(event string, handler func(...interface{}) rt.Value) *Listener {
|
||||
listener := &Listener{
|
||||
typ: goListener,
|
||||
caller: handler,
|
||||
}
|
||||
|
||||
b.addListener(event, listener)
|
||||
return listener
|
||||
}
|
||||
|
||||
// OnLua adds a Lua function handler for an event.
|
||||
func (b *Bait) OnLua(event string, handler *rt.Closure) *Listener {
|
||||
listener := &Listener{
|
||||
typ: luaListener,
|
||||
luaCaller: handler,
|
||||
}
|
||||
b.addListener(event, listener)
|
||||
|
||||
return listener
|
||||
}
|
||||
|
||||
// Off removes a Go function handler for an event.
|
||||
func (b *Bait) Off(event string, listener *Listener) {
|
||||
handles := b.handlers[event]
|
||||
|
||||
for i, handle := range handles {
|
||||
if handle == listener {
|
||||
b.removeListener(event, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bait) Loader(L *lua.LState) int {
|
||||
mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{})
|
||||
L.SetField(mod, "throw", luar.New(L, b.throw))
|
||||
L.SetField(mod, "catch", luar.New(L, b.catch))
|
||||
// OffLua removes a Lua function handler for an event.
|
||||
func (b *Bait) OffLua(event string, handler *rt.Closure) {
|
||||
handles := b.handlers[event]
|
||||
|
||||
L.Push(mod)
|
||||
|
||||
return 1
|
||||
for i, handle := range handles {
|
||||
if handle.luaCaller == handler {
|
||||
b.removeListener(event, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bait) throw(name string, args ...interface{}) {
|
||||
b.Em.Emit(name, args...)
|
||||
// Once adds a Go function listener for an event that only runs once.
|
||||
func (b *Bait) Once(event string, handler func(...interface{}) rt.Value) *Listener {
|
||||
listener := &Listener{
|
||||
typ: goListener,
|
||||
once: true,
|
||||
caller: handler,
|
||||
}
|
||||
b.addListener(event, listener)
|
||||
|
||||
return listener
|
||||
}
|
||||
|
||||
func (b *Bait) catch(name string, catcher func(interface{})) {
|
||||
b.Em.On(name, catcher)
|
||||
// OnceLua adds a Lua function listener for an event that only runs once.
|
||||
func (b *Bait) OnceLua(event string, handler *rt.Closure) *Listener {
|
||||
listener := &Listener{
|
||||
typ: luaListener,
|
||||
once: true,
|
||||
luaCaller: handler,
|
||||
}
|
||||
b.addListener(event, listener)
|
||||
|
||||
return listener
|
||||
}
|
||||
|
||||
// SetRecoverer sets the function to be executed when a panic occurs in an event.
|
||||
func (b *Bait) SetRecoverer(recoverer Recoverer) {
|
||||
b.recoverer = recoverer
|
||||
}
|
||||
|
||||
func (b *Bait) addListener(event string, listener *Listener) {
|
||||
if b.handlers[event] == nil {
|
||||
b.handlers[event] = []*Listener{}
|
||||
}
|
||||
|
||||
b.handlers[event] = append(b.handlers[event], listener)
|
||||
}
|
||||
|
||||
|
||||
func (b *Bait) removeListener(event string, idx int) {
|
||||
b.handlers[event][idx] = b.handlers[event][len(b.handlers[event]) - 1]
|
||||
|
||||
b.handlers[event] = b.handlers[event][:len(b.handlers[event]) - 1]
|
||||
}
|
||||
|
||||
func (b *Bait) callRecoverer(event string, handler *Listener, err interface{}) {
|
||||
if b.recoverer == nil {
|
||||
panic(err)
|
||||
}
|
||||
b.recoverer(event, handler, err)
|
||||
}
|
||||
|
||||
func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
exports := map[string]util.LuaExport{
|
||||
"catch": util.LuaExport{b.bcatch, 2, false},
|
||||
"catchOnce": util.LuaExport{b.bcatchOnce, 2, false},
|
||||
"throw": util.LuaExport{b.bthrow, 1, true},
|
||||
"release": util.LuaExport{b.brelease, 2, false},
|
||||
"hooks": util.LuaExport{b.bhooks, 1, false},
|
||||
}
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, exports)
|
||||
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
// catch(name, cb)
|
||||
// Catches an event. This function can be used to act on events.
|
||||
// #param name string The name of the hook.
|
||||
// #param cb function The function that will be called when the hook is thrown.
|
||||
/*
|
||||
#example
|
||||
bait.catch('hilbish.exit', function()
|
||||
print 'Goodbye Hilbish!'
|
||||
end)
|
||||
#example
|
||||
*/
|
||||
func (b *Bait) bcatch(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
name, catcher, err := util.HandleStrCallback(t, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.OnLua(name, catcher)
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// catchOnce(name, cb)
|
||||
// Catches an event, but only once. This will remove the hook immediately after it runs for the first time.
|
||||
// #param name string The name of the event
|
||||
// #param cb function The function that will be called when the event is thrown.
|
||||
func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
name, catcher, err := util.HandleStrCallback(t, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.OnceLua(name, catcher)
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// hooks(name) -> table
|
||||
// Returns a table of functions that are hooked on an event with the corresponding `name`.
|
||||
// #param name string The name of the hook
|
||||
// #returns table<function>
|
||||
func (b *Bait) bhooks(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
evName, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
noHooks := errors.New("no hooks for event " + evName)
|
||||
|
||||
handlers := b.handlers[evName]
|
||||
if handlers == nil {
|
||||
return nil, noHooks
|
||||
}
|
||||
|
||||
luaHandlers := rt.NewTable()
|
||||
for _, handler := range handlers {
|
||||
if handler.typ != luaListener { continue }
|
||||
luaHandlers.Set(rt.IntValue(luaHandlers.Len() + 1), rt.FunctionValue(handler.luaCaller))
|
||||
}
|
||||
|
||||
if luaHandlers.Len() == 0 {
|
||||
return nil, noHooks
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(luaHandlers)), nil
|
||||
}
|
||||
|
||||
// release(name, catcher)
|
||||
// Removes the `catcher` for the event with `name`.
|
||||
// For this to work, `catcher` has to be the same function used to catch
|
||||
// an event, like one saved to a variable.
|
||||
// #param name string Name of the event the hook is on
|
||||
// #param catcher function Hook function to remove
|
||||
/*
|
||||
#example
|
||||
local hookCallback = function() print 'hi' end
|
||||
|
||||
bait.catch('event', hookCallback)
|
||||
|
||||
-- a little while later....
|
||||
bait.release('event', hookCallback)
|
||||
-- and now hookCallback will no longer be ran for the event.
|
||||
#example
|
||||
*/
|
||||
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
name, catcher, err := util.HandleStrCallback(t, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.OffLua(name, catcher)
|
||||
|
||||
return c.Next(), nil
|
||||
}
|
||||
|
||||
// throw(name, ...args)
|
||||
// #param name string The name of the hook.
|
||||
// #param args ...any The arguments to pass to the hook.
|
||||
// Throws a hook with `name` with the provided `args`.
|
||||
/*
|
||||
#example
|
||||
bait.throw('greeting', 'world')
|
||||
|
||||
-- This can then be listened to via
|
||||
bait.catch('gretting', function(greetTo)
|
||||
print('Hello ' .. greetTo)
|
||||
end)
|
||||
#example
|
||||
*/
|
||||
func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ifaceSlice := make([]interface{}, len(c.Etc()))
|
||||
for i, v := range c.Etc() {
|
||||
ifaceSlice[i] = v
|
||||
}
|
||||
ret := b.Emit(name, ifaceSlice...)
|
||||
|
||||
return c.PushingNext(t.Runtime, ret...), nil
|
||||
}
|
||||
|
||||
@ -1,45 +1,133 @@
|
||||
// library for custom commands
|
||||
/*
|
||||
Commander is the library which handles Hilbish commands. This makes
|
||||
the user able to add Lua-written commands to their shell without making
|
||||
a separate script in a bin folder. Instead, you may simply use the Commander
|
||||
library in your Hilbish config.
|
||||
|
||||
```lua
|
||||
local commander = require 'commander'
|
||||
|
||||
commander.register('hello', function(args, sinks)
|
||||
sinks.out:writeln 'Hello world!'
|
||||
end)
|
||||
```
|
||||
|
||||
In this example, a command with the name of `hello` is created
|
||||
that will print `Hello world!` to output. One question you may
|
||||
have is: What is the `sinks` parameter?
|
||||
|
||||
The `sinks` parameter is a table with 3 keys: `input`, `out`, and `err`.
|
||||
There is an `in` alias to `input`, but it requires using the string accessor syntax (`sinks['in']`)
|
||||
as `in` is also a Lua keyword, so `input` is preferred for use.
|
||||
All of them are a @Sink.
|
||||
In the future, `sinks.in` will be removed.
|
||||
|
||||
- `in` is the standard input.
|
||||
You may use the read functions on this sink to get input from the user.
|
||||
- `out` is standard output.
|
||||
This is usually where command output should go.
|
||||
- `err` is standard error.
|
||||
This sink is for writing errors, as the name would suggest.
|
||||
*/
|
||||
package commander
|
||||
|
||||
import (
|
||||
"github.com/chuckpreslar/emission"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"hilbish/util"
|
||||
"hilbish/golibs/bait"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/arnodel/golua/lib/packagelib"
|
||||
)
|
||||
|
||||
type Commander struct{
|
||||
Events *emission.Emitter
|
||||
Events *bait.Bait
|
||||
Loader packagelib.Loader
|
||||
Commands map[string]*rt.Closure
|
||||
}
|
||||
|
||||
func New() Commander {
|
||||
return Commander{
|
||||
Events: emission.NewEmitter(),
|
||||
func New(rtm *rt.Runtime) *Commander {
|
||||
c := &Commander{
|
||||
Events: bait.New(rtm),
|
||||
Commands: make(map[string]*rt.Closure),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commander) Loader(L *lua.LState) int {
|
||||
var exports = map[string]lua.LGFunction{
|
||||
"register": c.register,
|
||||
"deregister": c.deregister,
|
||||
c.Loader = packagelib.Loader{
|
||||
Load: c.loaderFunc,
|
||||
Name: "commander",
|
||||
}
|
||||
mod := L.SetFuncs(L.NewTable(), exports)
|
||||
|
||||
L.Push(mod)
|
||||
|
||||
return 1
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Commander) register(L *lua.LState) int {
|
||||
cmdName := L.CheckString(1)
|
||||
cmd := L.CheckFunction(2)
|
||||
func (c *Commander) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
exports := map[string]util.LuaExport{
|
||||
"register": util.LuaExport{c.cregister, 2, false},
|
||||
"deregister": util.LuaExport{c.cderegister, 1, false},
|
||||
"registry": util.LuaExport{c.cregistry, 0, false},
|
||||
}
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, exports)
|
||||
|
||||
c.Events.Emit("commandRegister", cmdName, cmd)
|
||||
|
||||
return 0
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
func (c *Commander) deregister(L *lua.LState) int {
|
||||
cmdName := L.CheckString(1)
|
||||
// register(name, cb)
|
||||
// Adds a new command with the given `name`. When Hilbish has to run a command with a name,
|
||||
// it will run the function providing the arguments and sinks.
|
||||
// #param name string Name of the command
|
||||
// #param cb function Callback to handle command invocation
|
||||
/*
|
||||
#example
|
||||
-- When you run the command `hello` in the shell, it will print `Hello world`.
|
||||
-- If you run it with, for example, `hello Hilbish`, it will print 'Hello Hilbish'
|
||||
commander.register('hello', function(args, sinks)
|
||||
local name = 'world'
|
||||
if #args > 0 then name = args[1] end
|
||||
|
||||
c.Events.Emit("commandDeregister", cmdName)
|
||||
sinks.out:writeln('Hello ' .. name)
|
||||
end)
|
||||
#example
|
||||
*/
|
||||
func (c *Commander) cregister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||
cmdName, cmd, err := util.HandleStrCallback(t, ct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return 0
|
||||
c.Commands[cmdName] = cmd
|
||||
|
||||
return ct.Next(), err
|
||||
}
|
||||
|
||||
// deregister(name)
|
||||
// Removes the named command. Note that this will only remove Commander-registered commands.
|
||||
// #param name string Name of the command to remove.
|
||||
func (c *Commander) cderegister(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||
if err := ct.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmdName, err := ct.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(c.Commands, cmdName)
|
||||
|
||||
return ct.Next(), err
|
||||
}
|
||||
|
||||
// registry() -> table
|
||||
// Returns all registered commanders. Returns a list of tables with the following keys:
|
||||
// - `exec`: The function used to run the commander. Commanders require args and sinks to be passed.
|
||||
// #returns table
|
||||
func (c *Commander) cregistry(t *rt.Thread, ct *rt.GoCont) (rt.Cont, error) {
|
||||
registryLua := rt.NewTable()
|
||||
for cmdName, cmd := range c.Commands {
|
||||
cmdTbl := rt.NewTable()
|
||||
cmdTbl.Set(rt.StringValue("exec"), rt.FunctionValue(cmd))
|
||||
|
||||
registryLua.Set(rt.StringValue(cmdName), rt.TableValue(cmdTbl))
|
||||
}
|
||||
|
||||
return ct.PushingNext1(t.Runtime, rt.TableValue(registryLua)), nil
|
||||
}
|
||||
|
||||
345
golibs/fs/fs.go
@ -1,71 +1,332 @@
|
||||
// filesystem interaction and functionality library
|
||||
/*
|
||||
The fs module provides filesystem functions to Hilbish. While Lua's standard
|
||||
library has some I/O functions, they're missing a lot of the basics. The `fs`
|
||||
library offers more functions and will work on any operating system Hilbish does.
|
||||
#field pathSep The operating system's path separator.
|
||||
*/
|
||||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/yuin/gopher-lua"
|
||||
"layeh.com/gopher-luar"
|
||||
"hilbish/util"
|
||||
|
||||
rt "github.com/arnodel/golua/runtime"
|
||||
"github.com/arnodel/golua/lib/packagelib"
|
||||
"github.com/arnodel/golua/lib/iolib"
|
||||
)
|
||||
|
||||
func Loader(L *lua.LState) int {
|
||||
mod := L.SetFuncs(L.NewTable(), exports)
|
||||
|
||||
L.Push(mod)
|
||||
return 1
|
||||
var Loader = packagelib.Loader{
|
||||
Load: loaderFunc,
|
||||
Name: "fs",
|
||||
}
|
||||
|
||||
func LuaErr(L *lua.LState, code int) {
|
||||
// TODO: Error with a table, with path and error code
|
||||
L.Error(lua.LNumber(code), 2)
|
||||
func loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
|
||||
exports := map[string]util.LuaExport{
|
||||
"cd": util.LuaExport{fcd, 1, false},
|
||||
"mkdir": util.LuaExport{fmkdir, 2, false},
|
||||
"stat": util.LuaExport{fstat, 1, false},
|
||||
"readdir": util.LuaExport{freaddir, 1, false},
|
||||
"abs": util.LuaExport{fabs, 1, false},
|
||||
"basename": util.LuaExport{fbasename, 1, false},
|
||||
"dir": util.LuaExport{fdir, 1, false},
|
||||
"glob": util.LuaExport{fglob, 1, false},
|
||||
"join": util.LuaExport{fjoin, 0, true},
|
||||
"pipe": util.LuaExport{fpipe, 0, false},
|
||||
}
|
||||
mod := rt.NewTable()
|
||||
util.SetExports(rtm, mod, exports)
|
||||
mod.Set(rt.StringValue("pathSep"), rt.StringValue(string(os.PathSeparator)))
|
||||
mod.Set(rt.StringValue("pathListSep"), rt.StringValue(string(os.PathListSeparator)))
|
||||
|
||||
return rt.TableValue(mod), nil
|
||||
}
|
||||
|
||||
var exports = map[string]lua.LGFunction{
|
||||
"cd": cd,
|
||||
"mkdir": mkdir,
|
||||
"stat": stat,
|
||||
}
|
||||
|
||||
func cd(L *lua.LState) int {
|
||||
path := L.CheckString(1)
|
||||
|
||||
err := os.Chdir(strings.TrimSpace(path))
|
||||
// abs(path) -> string
|
||||
// Returns an absolute version of the `path`.
|
||||
// This can be used to resolve short paths like `..` to `/home/user`.
|
||||
// #param path string
|
||||
// #returns string
|
||||
func fabs(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
path, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
switch e := err.(*os.PathError).Err.Error(); e {
|
||||
case "no such file or directory":
|
||||
LuaErr(L, 1)
|
||||
default:
|
||||
fmt.Printf("Found unhandled error case: %s", e)
|
||||
fmt.Printf("Report this at https://github.com/Hilbis/Hilbish/issues with the title being: \"fs: unahndled error case %s\", and show what caused it.\n", e)
|
||||
LuaErr(L, 213)
|
||||
return nil, err
|
||||
}
|
||||
path = util.ExpandHome(path)
|
||||
|
||||
abspath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.StringValue(abspath)), nil
|
||||
}
|
||||
|
||||
// basename(path) -> string
|
||||
// Returns the "basename," or the last part of the provided `path`. If path is empty,
|
||||
// `.` will be returned.
|
||||
// #param path string Path to get the base name of.
|
||||
// #returns string
|
||||
func fbasename(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Base(path))), nil
|
||||
}
|
||||
|
||||
// cd(dir)
|
||||
// Changes Hilbish's directory to `dir`.
|
||||
// #param dir string Path to change directory to.
|
||||
func fcd(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path = util.ExpandHome(strings.TrimSpace(path))
|
||||
oldWd, _ := os.Getwd()
|
||||
|
||||
abspath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = os.Chdir(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
util.DoString(t.Runtime, fmt.Sprintf(`
|
||||
local bait = require 'bait'
|
||||
bait.throw('hilbish.cd', '%s', '%s')
|
||||
`, abspath, oldWd))
|
||||
|
||||
return c.Next(), err
|
||||
}
|
||||
|
||||
// dir(path) -> string
|
||||
// Returns the directory part of `path`. If a file path like
|
||||
// `~/Documents/doc.txt` then this function will return `~/Documents`.
|
||||
// #param path string Path to get the directory for.
|
||||
// #returns string
|
||||
func fdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.PushingNext(t.Runtime, rt.StringValue(filepath.Dir(path))), nil
|
||||
}
|
||||
|
||||
// glob(pattern) -> matches (table)
|
||||
// Match all files based on the provided `pattern`.
|
||||
// For the syntax' refer to Go's filepath.Match function: https://pkg.go.dev/path/filepath#Match
|
||||
// #param pattern string Pattern to compare files with.
|
||||
// #returns table A list of file names/paths that match.
|
||||
/*
|
||||
#example
|
||||
--[[
|
||||
Within a folder that contains the following files:
|
||||
a.txt
|
||||
init.lua
|
||||
code.lua
|
||||
doc.pdf
|
||||
]]--
|
||||
local matches = fs.glob './*.lua'
|
||||
print(matches)
|
||||
-- -> {'init.lua', 'code.lua'}
|
||||
#example
|
||||
*/
|
||||
func fglob(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pattern, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
luaMatches := rt.NewTable()
|
||||
|
||||
for i, match := range matches {
|
||||
luaMatches.Set(rt.IntValue(int64(i + 1)), rt.StringValue(match))
|
||||
}
|
||||
|
||||
return c.PushingNext(t.Runtime, rt.TableValue(luaMatches)), nil
|
||||
}
|
||||
|
||||
// join(...path) -> string
|
||||
// Takes any list of paths and joins them based on the operating system's path separator.
|
||||
// #param path ...string Paths to join together
|
||||
// #returns string The joined path.
|
||||
/*
|
||||
#example
|
||||
-- This prints the directory for Hilbish's config!
|
||||
print(fs.join(hilbish.userDir.config, 'hilbish'))
|
||||
-- -> '/home/user/.config/hilbish' on Linux
|
||||
#example
|
||||
*/
|
||||
func fjoin(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
strs := make([]string, len(c.Etc()))
|
||||
for i, v := range c.Etc() {
|
||||
if v.Type() != rt.StringType {
|
||||
// +2; go indexes of 0 and first arg from above
|
||||
return nil, fmt.Errorf("bad argument #%d to run (expected string, got %s)", i + 1, v.TypeName())
|
||||
}
|
||||
strs[i] = v.AsString()
|
||||
}
|
||||
|
||||
return 0
|
||||
res := filepath.Join(strs...)
|
||||
|
||||
return c.PushingNext(t.Runtime, rt.StringValue(res)), nil
|
||||
}
|
||||
|
||||
func mkdir(L *lua.LState) int {
|
||||
dirname := L.CheckString(1)
|
||||
recursive := L.ToBool(2)
|
||||
path := strings.TrimSpace(dirname)
|
||||
// mkdir(name, recursive)
|
||||
// Creates a new directory with the provided `name`.
|
||||
// With `recursive`, mkdir will create parent directories.
|
||||
// #param name string Name of the directory
|
||||
// #param recursive boolean Whether to create parent directories for the provided name
|
||||
/*
|
||||
#example
|
||||
-- This will create the directory foo, then create the directory bar in the
|
||||
-- foo directory. If recursive is false in this case, it will fail.
|
||||
fs.mkdir('./foo/bar', true)
|
||||
#example
|
||||
*/
|
||||
func fmkdir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
recursive, err := c.BoolArg(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path = util.ExpandHome(strings.TrimSpace(path))
|
||||
|
||||
// TODO: handle error here
|
||||
if recursive {
|
||||
os.MkdirAll(path, 0744)
|
||||
err = os.MkdirAll(path, 0744)
|
||||
} else {
|
||||
os.Mkdir(path, 0744)
|
||||
err = os.Mkdir(path, 0744)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return 0
|
||||
return c.Next(), err
|
||||
}
|
||||
|
||||
func stat(L *lua.LState) int {
|
||||
path := L.CheckString(1)
|
||||
// fpipe() -> File, File
|
||||
// Returns a pair of connected files, also known as a pipe.
|
||||
// The type returned is a Lua file, same as returned from `io` functions.
|
||||
// #returns File
|
||||
// #returns File
|
||||
func fpipe(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
rf, wf, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: handle error here
|
||||
pathinfo, _ := os.Stat(path)
|
||||
L.Push(luar.New(L, pathinfo))
|
||||
rfLua := iolib.NewFile(rf, 0)
|
||||
wfLua := iolib.NewFile(wf, 0)
|
||||
|
||||
return 1
|
||||
return c.PushingNext(t.Runtime, rfLua.Value(t.Runtime), wfLua.Value(t.Runtime)), nil
|
||||
}
|
||||
// readdir(path) -> table[string]
|
||||
// Returns a list of all files and directories in the provided path.
|
||||
// #param dir string
|
||||
// #returns table
|
||||
func freaddir(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dir = util.ExpandHome(dir)
|
||||
names := rt.NewTable()
|
||||
|
||||
dirEntries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, entry := range dirEntries {
|
||||
names.Set(rt.IntValue(int64(i + 1)), rt.StringValue(entry.Name()))
|
||||
}
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(names)), nil
|
||||
}
|
||||
|
||||
// stat(path) -> {}
|
||||
// Returns the information about a given `path`.
|
||||
// The returned table contains the following values:
|
||||
// name (string) - Name of the path
|
||||
// size (number) - Size of the path in bytes
|
||||
// mode (string) - Unix permission mode in an octal format string (with leading 0)
|
||||
// isDir (boolean) - If the path is a directory
|
||||
// #param path string
|
||||
// #returns table
|
||||
/*
|
||||
#example
|
||||
local inspect = require 'inspect'
|
||||
|
||||
local stat = fs.stat '~'
|
||||
print(inspect(stat))
|
||||
--[[
|
||||
Would print the following:
|
||||
{
|
||||
isDir = true,
|
||||
mode = "0755",
|
||||
name = "username",
|
||||
size = 12288
|
||||
}
|
||||
]]--
|
||||
#example
|
||||
*/
|
||||
func fstat(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.Check1Arg(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path, err := c.StringArg(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path = util.ExpandHome(path)
|
||||
|
||||
pathinfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statTbl := rt.NewTable()
|
||||
statTbl.Set(rt.StringValue("name"), rt.StringValue(pathinfo.Name()))
|
||||
statTbl.Set(rt.StringValue("size"), rt.IntValue(pathinfo.Size()))
|
||||
statTbl.Set(rt.StringValue("mode"), rt.StringValue("0" + strconv.FormatInt(int64(pathinfo.Mode().Perm()), 8)))
|
||||
statTbl.Set(rt.StringValue("isDir"), rt.BoolValue(pathinfo.IsDir()))
|
||||
|
||||
return c.PushingNext1(t.Runtime, rt.TableValue(statTbl)), nil
|
||||
}
|
||||
|
||||
|
||||
122
golibs/readline/CHANGES.md
Normal file
@ -0,0 +1,122 @@
|
||||
## Changes
|
||||
|
||||
### 4.1.0
|
||||
---------
|
||||
|
||||
Many new features and improvements in this version:
|
||||
- New keybindings (working on Emacs, and in `Vim Insert Mode`):
|
||||
* `CtrlW` to cut the previous word at the cursor
|
||||
* `CtrlA` to go back to the beginning of the line
|
||||
* `CtrlY` to paste the laste copy/paste buffer (see Registers)
|
||||
* `CtrlU` to cut the whole line.
|
||||
|
||||
- More precise Vim iterations:
|
||||
* Iterations can now be applied to some Vim actions (`y4w`, `d3b`)
|
||||
|
||||
- Implemented Vim registers:
|
||||
* Yank/paste operations of any sort can occur and be assigned to registers.
|
||||
* The default `""` register
|
||||
* 10 numbered registers, to which bufffers are automatically added
|
||||
* 26 lettered registers (lowercase), to which you can append with `"D` (D being the uppercase of the `"d` register)
|
||||
* Triggered in Insert Mode with `Alt"` (buggy sometimes: goes back to Normal mode selecting a register, will have to fix this)
|
||||
|
||||
- Unified iterations and registers:
|
||||
* To copy to the `d` register the next 4 words: `"d y4w`
|
||||
* To append to this `d` register the cuttend end of line: `"D d$"`
|
||||
* In this example, the `d` register buffer is also the buffer in the default register `""`
|
||||
* You could either:
|
||||
- Paste 3 times this buffer while in Normal mode: `3p`
|
||||
- Paste the buffer once in Insert mode: `CtrlY`
|
||||
|
||||
- History completions:
|
||||
* The binding for the alternative history changed to `AltR` (the normal remains `CtrlR`)
|
||||
* By defaul the history filters only against the search pattern.
|
||||
* If there are matches for this patten, the first occurence is insert (virtually)
|
||||
* This is refreshed as the pattern changes
|
||||
* `CtrlG` to exit the comps, while leaving the current candidate
|
||||
* `CtrlC` to exit and delete the current candidate
|
||||
|
||||
- Completions:
|
||||
* When a candidate is inserted virtually, `CtrlC` to abort both completions and the candidate
|
||||
* Implemented global printing size: If the overall number of completions is biffer, will roll over them.
|
||||
|
||||
**Notes:**
|
||||
* The `rl.Readline()` function dispatch has some big cases, maybe a bit of refactoring would be nice
|
||||
* The way the buffer storing bytes from key strokes sometimes gives weird results (like `Alt"` for showing Vim registers)
|
||||
* Some defer/cancel calls related to DelayedTabContext that should have been merged from lmorg/readline are still missing.
|
||||
|
||||
|
||||
### 4.0.0-beta
|
||||
---------
|
||||
|
||||
This version is the merge of [maxlandon/readline](https://github.com/maxlandon/readline)
|
||||
and [lmorg/readline](https://github.com/lmorg/readline). Therefore it both integrates parts
|
||||
from both libraries, but also adds a few features, with some API breaking changes (ex: completions),
|
||||
thus the new 4.0.0 version. Remains a beta because maxlandon/readline code has not been thoroughly
|
||||
test neither in nor of itself, and no more against `lmorg/murex`, it's main consumer until now.
|
||||
|
||||
#### Code
|
||||
- Enhance delete/copy buffer in Vim mode
|
||||
- DelayedTabContext now works with completion groups
|
||||
|
||||
#### Packages
|
||||
- Added a `completers` package, with a default tab/hint/syntax completer working with
|
||||
the [go-flags](https://github.com/jessevdk/go-flags) library.
|
||||
- The `examples` package has been enhanced with a more complete -base- application code. See the wiki
|
||||
|
||||
#### Documentation
|
||||
- Merged relevant parts of both READMEs
|
||||
- Use documentation from maxlandon/readline
|
||||
|
||||
#### New features / bindings
|
||||
- CtrlL now clears the screen and reprints the prompt
|
||||
- Added evilsocket's tui colors/effects, for ease of use & integration with shell. Has not yet replaced the current `seqColor` variables everywhere though
|
||||
|
||||
#### Changes I'm not sure of
|
||||
- is the function leftMost() in cursor.go useful ?
|
||||
- is the function getCursorPos() in cursor.go useful ?
|
||||
|
||||
|
||||
### 3.0.0
|
||||
---------
|
||||
|
||||
- Added test (input line, prompt, correct refresh, etc)
|
||||
- Added multiline support
|
||||
- Added `DelayedTabContext` and `DelayedSyntaxWorker`
|
||||
|
||||
|
||||
### 2.1.0
|
||||
---------
|
||||
|
||||
Error returns from `readline` have been created as error a variable, which is
|
||||
more idiomatic to Go than the err constants that existed previously. Currently
|
||||
both are still available to use however I will be deprecating the the constants
|
||||
in a latter release.
|
||||
|
||||
**Deprecated constants:**
|
||||
```go
|
||||
const (
|
||||
// ErrCtrlC is returned when ctrl+c is pressed
|
||||
ErrCtrlC = "Ctrl+C"
|
||||
|
||||
// ErrEOF is returned when ctrl+d is pressed
|
||||
ErrEOF = "EOF"
|
||||
)
|
||||
```
|
||||
|
||||
**New error variables:**
|
||||
```go
|
||||
var (
|
||||
// CtrlC is returned when ctrl+c is pressed
|
||||
CtrlC = errors.New("Ctrl+C")
|
||||
|
||||
// EOF is returned when ctrl+d is pressed
|
||||
// (this is actually the same value as io.EOF)
|
||||
EOF = errors.New("EOF")
|
||||
)
|
||||
```
|
||||
|
||||
## Version Information
|
||||
|
||||
`readline`'s version numbers are based on Semantic Versioning. More details can
|
||||
be found in the [README.md](README.md#version-information).
|
||||
201
golibs/readline/LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
165
golibs/readline/README.md
Normal file
@ -0,0 +1,165 @@
|
||||
|
||||
# Readline - Console library in Go
|
||||
|
||||

|
||||
*This demo GIF has been made with a Sliver project client.*
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
**This project is actually the merging of an original project (github.com/lmorg/readline) and one of its
|
||||
forks (github.com/maxlandon/readline): both introductions are thus here given, in chronological order.**
|
||||
|
||||
#### lmorg
|
||||
|
||||
This project began a few years prior to this git commit history as an API for
|
||||
_[murex](https://github.com/lmorg/murex)_, an alternative UNIX shell, because
|
||||
I wasn't satisfied with the state of existing Go packages for readline (at that
|
||||
time they were either bugger and/or poorly maintained, or lacked features I
|
||||
desired). The state of things for readline in Go may have changed since then
|
||||
however own package had also matured and grown to include many more features
|
||||
that has arisen during the development of _murex_. So it seemed only fair to
|
||||
give back to the community considering it was other peoples readline libraries
|
||||
that allowed me rapidly prototype _murex_ during it's early stages of
|
||||
development.
|
||||
|
||||
#### maxlandon
|
||||
|
||||
This project started out of the wish to make an enhanced console for a security tool (Sliver, see below).
|
||||
There are already several readline libraries available in Go ([github.com/chzyer/readline](https://github.com/chzyer/readline),
|
||||
and [github.com/lmorg/readline](https://github.com/lmorg/readline)), but being stricter readline implementations, their completion
|
||||
|
||||
|
||||
## Features Summary
|
||||
|
||||
This project is not an integrated REPL/command-line application, which means it does not automatically understand nor executes any commands.
|
||||
However, having been developed in a project using the CLI [github.com/jessevdk/go-flags](https://github.com/jessevdk/go-flags) library,
|
||||
it also includes some default utilities (completers) that are made to work with this library, which I humbly but highly recommend.
|
||||
Please see the [Wiki](https://github.com/maxlandon/readline/wiki) (or the `examples/` directory) for information on how to use these utilities.
|
||||
|
||||
A summarized list of features supported by this library is the following:
|
||||
|
||||
### Input & Editing
|
||||
- Vim / Emacs input and editing modes.
|
||||
- Optional, live-refresh Vim status.
|
||||
- Vim modes (Insert, Normal, Replace, Delete) with visual prompt Vim status indicator
|
||||
- line editing using `$EDITOR` (`vi` in the example - enabled by pressing `[ESC]` followed by `[v]`)
|
||||
- Vim registers (one default, 10 numbered, and 26 lettered)
|
||||
- Vim iterations
|
||||
- Most default keybindings you might find in Emacs-like readline. Some are still missing though
|
||||
|
||||
### Completion engine
|
||||
- 3 types of completion categories (`Grid`, `List` and `Map`)
|
||||
- Stackable, combinable completions (completion groups of any type & size can be proposed simultaneously).
|
||||
- Controlable completion group sizes (if size is greater than completions, the completions will roll automatically)
|
||||
- Virtual insertion of the current candidate, like in Z-shell.
|
||||
- In `List` completion groups, ability to have alternative candidates (used for displaying `--long` and `-s` (short) options, with descriptions)
|
||||
- Completions working anywhere in the input line (your cursor can be anywhere)
|
||||
- Completions are searchable with *Ctrl-F*, like in lmorg's library.
|
||||
|
||||
### Prompt system & Colors
|
||||
- 1-line and 2-line prompts, both being customizable.
|
||||
- Functions for refreshing the prompt, with optional behavior settings.
|
||||
- Optional colors (can be disabled).
|
||||
|
||||
### Hints & Syntax highlighting
|
||||
- A hint line can be printed below the input line, with any type of information. See utilities for a default one.
|
||||
- The Hint system is now refreshed depending on the cursor position as well, like completions.
|
||||
- A syntax highlighting system. A default one is also available.
|
||||
|
||||
### Command history
|
||||
- Ability to have 2 different history sources (I used this for clients connected to a server, used by a single user).
|
||||
- History is searchable like completions.
|
||||
- Default history is an in-memory list.
|
||||
- Quick history navigation with *Up*/*Down* arrow keys in Emacs mode, and *j*/*k* keys in Vim mode.
|
||||
|
||||
### Utilities
|
||||
- Default Tab completer, Hint formatter and Syntax highlighter provided, using [github.com/jessevdk/go-flags](https://github.com/jessevdk/go-flags)
|
||||
command parser to build themselves. These are in the `completers/` directory. Please look at the [Wiki page](https://github.com/maxlandon/readline/wiki)
|
||||
for how to use them. Also feel free to use them as an inspiration source to make your owns.
|
||||
- Colors mercilessly copied from [github.com/evilsocket/islazy/](https://github.com/evilsocket/islazy) `tui/` package.
|
||||
- Also in the `completers` directory, completion functions for environment variables (using Go's std lib for getting them), and dir/file path completions.
|
||||
|
||||
|
||||
## Installation & Usage
|
||||
|
||||
As usual with Go, installation:
|
||||
```
|
||||
go get github.com/maxlandon/readline
|
||||
```
|
||||
Please see either the `examples` directory, or the Wiki for detailed instructions on how to use this library.
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
The complete documentation for this library can be found in the repo's [Wiki](https://github.com/maxlandon/readline/wiki). Below is the Table of Contents:
|
||||
|
||||
**Getting started**
|
||||
* [ Embedding readline in a project ](https://github.com/maxlandon/readline/wiki/Embedding-Readline-In-A-Project)
|
||||
* [ Input Modes ](https://github.com/maxlandon/readline/wiki/Input-Modes)
|
||||
|
||||
**Prompt system**
|
||||
* [ Setting the Prompts](https://github.com/maxlandon/readline/wiki/Prompt-Setup)
|
||||
* [ Prompt Refresh ](https://github.com/maxlandon/readline/wiki/Prompt-Refresh)
|
||||
|
||||
**Completion Engine**
|
||||
* [ Completion Groups ](https://github.com/maxlandon/readline/wiki/Completion-Groups)
|
||||
* [ Completion Search ](https://github.com/maxlandon/readline/wiki/Completion-Search)
|
||||
|
||||
**Hint Formatter & Syntax Highlighter**
|
||||
* [ Live Refresh Demonstration ](https://github.com/maxlandon/readline/wiki/Live-Refresh-Demonstration)
|
||||
|
||||
**Command History**
|
||||
* [ Main & Alternative Sources ](https://github.com/maxlandon/readline/wiki/Main-&-Alternative-Sources)
|
||||
* [ Navigation & Search ](https://github.com/maxlandon/readline/wiki/Navigation-&-Search)
|
||||
|
||||
#### Command & Completion utilities
|
||||
* [ Interfacing with the go-flags library](https://github.com/maxlandon/readline/wiki/Interfacing-With-Go-Flags)
|
||||
* [ Declaring go-flags commands](https://github.com/maxlandon/readline/wiki/Declaring-Commands)
|
||||
* [ Colors/Effects Usage ](https://github.com/maxlandon/readline/wiki/Colors-&-Effects-Usage)
|
||||
|
||||
|
||||
## Project Status & Support
|
||||
|
||||
Being alone working on this project and having only one lifetime (anyone able to solve this please call me), I can engage myself over the following:
|
||||
- Support for any issue opened.
|
||||
- Answering any questions related.
|
||||
- Being available for any blame you'd like to make for my humble but passioned work. I don't mind, I need to go up.
|
||||
|
||||
|
||||
## Version Information
|
||||
|
||||
* The version string will be based on Semantic Versioning. ie version numbers
|
||||
will be formatted `(major).(minor).(patch)` - for example `2.0.1`
|
||||
|
||||
* `major` releases _will_ have breaking changes. Be sure to read CHANGES.md for
|
||||
upgrade instructions
|
||||
|
||||
* `minor` releases will contain new APIs or introduce new user facing features
|
||||
which may affect useability from an end user perspective. However `minor`
|
||||
releases will not break backwards compatibility at the source code level and
|
||||
nor will it break existing expected user-facing behavior. These changes will
|
||||
be documented in CHANGES.md too
|
||||
|
||||
* `patch` releases will be bug fixes and such like. Where the code has changed
|
||||
but both API endpoints and user experience will remain the same (except where
|
||||
expected user experience was broken due to a bug, then that would be bumped
|
||||
to either a `minor` or `major` depending on the significance of the bug and
|
||||
the significance of the change to the user experience)
|
||||
|
||||
* Any updates to documentation, comments within code or the example code will
|
||||
not result in a version bump because they will not affect the output of the
|
||||
go compiler. However if this concerns you then I recommend pinning your
|
||||
project to the git commit hash rather than a `patch` release
|
||||
|
||||
## License Information
|
||||
|
||||
The `readline` library is distributed under the Apache License (Version 2.0, January 2004) (http://www.apache.org/licenses/).
|
||||
All the example code and documentation in `/examples`, `/completers` is public domain.
|
||||
|
||||
|
||||
## Warmest Thanks
|
||||
|
||||
- The [Sliver](https://github.com/BishopFox/sliver) implant framework project, which I used as a basis to make, test and refine this library. as well as all the GIFs and documentation pictures !
|
||||
- [evilsocket](https://github.com/evilsocket) for his TUI library !
|
||||
|
||||
237
golibs/readline/codes.go
Normal file
@ -0,0 +1,237 @@
|
||||
package readline
|
||||
|
||||
import "os"
|
||||
|
||||
// Character codes
|
||||
const (
|
||||
charCtrlA = iota + 1
|
||||
charCtrlB
|
||||
charCtrlC
|
||||
charEOF
|
||||
charCtrlE
|
||||
charCtrlF
|
||||
charCtrlG
|
||||
charBackspace // ISO 646
|
||||
charTab
|
||||
charCtrlJ
|
||||
charCtrlK
|
||||
charCtrlL
|
||||
charCtrlM
|
||||
charCtrlN
|
||||
charCtrlO
|
||||
charCtrlP
|
||||
charCtrlQ
|
||||
charCtrlR
|
||||
charCtrlS
|
||||
charCtrlT
|
||||
charCtrlU
|
||||
charCtrlV
|
||||
charCtrlW
|
||||
charCtrlX
|
||||
charCtrlY
|
||||
charCtrlZ
|
||||
charEscape
|
||||
charCtrlSlash // ^\
|
||||
charCtrlCloseSquare // ^]
|
||||
charCtrlHat // ^^
|
||||
charCtrlUnderscore // ^_
|
||||
charBackspace2 = 127 // ASCII 1963
|
||||
)
|
||||
|
||||
// Escape sequences
|
||||
var (
|
||||
seqUp = string([]byte{27, 91, 65})
|
||||
seqDown = string([]byte{27, 91, 66})
|
||||
seqForwards = string([]byte{27, 91, 67})
|
||||
seqBackwards = string([]byte{27, 91, 68})
|
||||
seqHome = string([]byte{27, 91, 72})
|
||||
seqHomeSc = string([]byte{27, 91, 49, 126})
|
||||
seqEnd = string([]byte{27, 91, 70})
|
||||
seqEndSc = string([]byte{27, 91, 52, 126})
|
||||
seqDelete = string([]byte{27, 91, 51, 126})
|
||||
seqDelete2 = string([]byte{27, 91, 80})
|
||||
seqCtrlDelete = string([]byte{27, 91, 51, 59, 53, 126})
|
||||
seqCtrlDelete2 = string([]byte{27, 91, 77})
|
||||
seqAltDelete = string([]byte{27, 91, 51, 59, 51, 126})
|
||||
seqShiftTab = string([]byte{27, 91, 90})
|
||||
seqAltQuote = string([]byte{27, 34}) // Added for showing registers ^["
|
||||
seqAltB = string([]byte{27, 98})
|
||||
seqAltD = string([]byte{27, 100})
|
||||
seqAltF = string([]byte{27, 102})
|
||||
seqAltR = string([]byte{27, 114}) // Used for alternative history
|
||||
seqAltBackspace = string([]byte{27, 127})
|
||||
seqPageUp = string([]byte{27, 91, 53, 126})
|
||||
seqPageDown = string([]byte{27, 91, 54, 126})
|
||||
)
|
||||
|
||||
const (
|
||||
seqPosSave = "\x1b[s"
|
||||
seqPosRestore = "\x1b[u"
|
||||
|
||||
seqClearLineAfer = "\x1b[0K"
|
||||
seqClearLineBefore = "\x1b[1K"
|
||||
seqClearLine = "\x1b[2K"
|
||||
seqClearScreenBelow = "\x1b[0J"
|
||||
seqClearScreen = "\x1b[2J" // Clears screen fully
|
||||
seqCursorTopLeft = "\x1b[H" // Clears screen and places cursor on top-left
|
||||
|
||||
seqGetCursorPos = "\x1b6n" // response: "\x1b{Line};{Column}R"
|
||||
seqHideCursor = "\x1b[?25l"
|
||||
seqUnhideCursor = "\x1b[?25h"
|
||||
|
||||
seqCtrlLeftArrow = "\x1b[1;5D"
|
||||
seqCtrlRightArrow = "\x1b[1;5C"
|
||||
|
||||
// seqAltQuote = "\x1b\"" // trigger registers list
|
||||
)
|
||||
|
||||
// Text effects
|
||||
const (
|
||||
seqReset = "\x1b[0m"
|
||||
seqBold = "\x1b[1m"
|
||||
seqUnderscore = "\x1b[4m"
|
||||
seqBlink = "\x1b[5m"
|
||||
seqInvert = "\x1b[7m"
|
||||
)
|
||||
|
||||
// Text colours
|
||||
const (
|
||||
seqFgBlack = "\x1b[30m"
|
||||
seqFgRed = "\x1b[31m"
|
||||
seqFgGreen = "\x1b[32m"
|
||||
seqFgYellow = "\x1b[33m"
|
||||
seqFgBlue = "\x1b[34m"
|
||||
seqFgMagenta = "\x1b[35m"
|
||||
seqFgCyan = "\x1b[36m"
|
||||
seqFgWhite = "\x1b[37m"
|
||||
|
||||
seqFgBlackBright = "\x1b[1;30m"
|
||||
seqFgRedBright = "\x1b[1;31m"
|
||||
seqFgGreenBright = "\x1b[1;32m"
|
||||
seqFgYellowBright = "\x1b[1;33m"
|
||||
seqFgBlueBright = "\x1b[1;34m"
|
||||
seqFgMagentaBright = "\x1b[1;35m"
|
||||
seqFgCyanBright = "\x1b[1;36m"
|
||||
seqFgWhiteBright = "\x1b[1;37m"
|
||||
)
|
||||
|
||||
// Background colours
|
||||
const (
|
||||
seqBgBlack = "\x1b[40m"
|
||||
seqBgRed = "\x1b[41m"
|
||||
seqBgGreen = "\x1b[42m"
|
||||
seqBgYellow = "\x1b[43m"
|
||||
seqBgBlue = "\x1b[44m"
|
||||
seqBgMagenta = "\x1b[45m"
|
||||
seqBgCyan = "\x1b[46m"
|
||||
seqBgWhite = "\x1b[47m"
|
||||
|
||||
seqBgBlackBright = "\x1b[1;40m"
|
||||
seqBgRedBright = "\x1b[1;41m"
|
||||
seqBgGreenBright = "\x1b[1;42m"
|
||||
seqBgYellowBright = "\x1b[1;43m"
|
||||
seqBgBlueBright = "\x1b[1;44m"
|
||||
seqBgMagentaBright = "\x1b[1;45m"
|
||||
seqBgCyanBright = "\x1b[1;46m"
|
||||
seqBgWhiteBright = "\x1b[1;47m"
|
||||
)
|
||||
|
||||
// Xterm 256 colors
|
||||
const (
|
||||
seqCtermFg255 = "\033[48;5;255m"
|
||||
)
|
||||
|
||||
// TODO: return whether its actually a sequence or not
|
||||
// remedies the edge case of someone literally typing Ctrl-A for example.
|
||||
func (rl *Readline) ReadChar() string {
|
||||
b := make([]byte, 1024)
|
||||
i, _ := os.Stdin.Read(b)
|
||||
r := []rune(string(b))
|
||||
s := string(r[:i])
|
||||
|
||||
switch b[0] {
|
||||
case charCtrlA:
|
||||
return "Ctrl-A"
|
||||
case charCtrlB:
|
||||
return "Ctrl-B"
|
||||
case charCtrlC:
|
||||
return "Ctrl-C"
|
||||
case charEOF:
|
||||
return "Ctrl-D"
|
||||
case charCtrlE:
|
||||
return "Ctrl-E"
|
||||
case charCtrlF:
|
||||
return "Ctrl-F"
|
||||
case charCtrlG:
|
||||
return "Ctrl-G"
|
||||
case charBackspace, charBackspace2:
|
||||
return "Backspace"
|
||||
case charTab:
|
||||
return "Tab"
|
||||
case charCtrlK:
|
||||
return "Ctrl-K"
|
||||
case charCtrlL:
|
||||
return "Ctrl-L"
|
||||
case charCtrlN:
|
||||
return "Ctrl-N"
|
||||
case charCtrlO:
|
||||
return "Ctrl-O"
|
||||
case charCtrlP:
|
||||
return "Ctrl-P"
|
||||
case charCtrlQ:
|
||||
return "Ctrl-Q"
|
||||
case charCtrlR:
|
||||
return "Ctrl-R"
|
||||
case charCtrlS:
|
||||
return "Ctrl-S"
|
||||
case charCtrlT:
|
||||
return "Ctrl-T"
|
||||
case charCtrlU:
|
||||
return "Ctrl-U"
|
||||
case charCtrlV:
|
||||
return "Ctrl-V"
|
||||
case charCtrlW:
|
||||
return "Ctrl-W"
|
||||
case charCtrlX:
|
||||
return "Ctrl-X"
|
||||
case charCtrlY:
|
||||
return "Ctrl-Y"
|
||||
case charCtrlZ:
|
||||
return "Ctrl-Z"
|
||||
case '\r':
|
||||
fallthrough
|
||||
case '\n':
|
||||
return "Enter"
|
||||
case charEscape:
|
||||
switch s {
|
||||
case string(charEscape):
|
||||
return "Escape"
|
||||
case seqUp:
|
||||
return "Up"
|
||||
case seqDown:
|
||||
return "Down"
|
||||
case seqBackwards:
|
||||
return "Left"
|
||||
case seqForwards:
|
||||
return "Right"
|
||||
case seqCtrlLeftArrow:
|
||||
return "Ctrl-Left"
|
||||
case seqCtrlRightArrow:
|
||||
return "Ctrl-Right"
|
||||
case seqCtrlDelete, seqCtrlDelete2:
|
||||
return "Ctrl-Delete"
|
||||
case seqHome, seqHomeSc:
|
||||
return "Home"
|
||||
case seqEnd, seqEndSc:
|
||||
return "End"
|
||||
case seqDelete, seqDelete2:
|
||||
return "Delete"
|
||||
case seqPageUp:
|
||||
return "Page-Up"
|
||||
case seqPageDown:
|
||||
return "Page-Down"
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
154
golibs/readline/comp-grid.go
Normal file
@ -0,0 +1,154 @@
|
||||
package readline
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// initGrid - Grid display details. Called each time we want to be sure to have
|
||||
// a working completion group either immediately, or later on. Generally defered.
|
||||
func (g *CompletionGroup) initGrid(rl *Readline) {
|
||||
|
||||
// Compute size of each completion item box
|
||||
tcMaxLength := 1
|
||||
for i := range g.Suggestions {
|
||||
if uniseg.GraphemeClusterCount(g.Suggestions[i]) > tcMaxLength {
|
||||
tcMaxLength = uniseg.GraphemeClusterCount(g.Suggestions[i])
|
||||
}
|
||||
}
|
||||
|
||||
g.tcPosX = 0
|
||||
g.tcPosY = 1
|
||||
g.tcOffset = 0
|
||||
|
||||
// Max number of columns
|
||||
g.tcMaxX = GetTermWidth() / (tcMaxLength + 2)
|
||||
if g.tcMaxX < 1 {
|
||||
g.tcMaxX = 1 // avoid a divide by zero error
|
||||
}
|
||||
|
||||
// Maximum number of lines
|
||||
maxY := len(g.Suggestions) / g.tcMaxX
|
||||
rest := len(g.Suggestions) % g.tcMaxX
|
||||
if rest != 0 {
|
||||
// if rest != 0 && maxY != 1 {
|
||||
maxY++
|
||||
}
|
||||
if maxY > g.MaxLength {
|
||||
g.tcMaxY = g.MaxLength
|
||||
} else {
|
||||
g.tcMaxY = maxY
|
||||
}
|
||||
}
|
||||
|
||||
// moveTabGridHighlight - Moves the highlighting for currently selected completion item (grid display)
|
||||
func (g *CompletionGroup) moveTabGridHighlight(rl *Readline, x, y int) (done bool, next bool) {
|
||||
|
||||
g.tcPosX += x
|
||||
g.tcPosY += y
|
||||
|
||||
// Columns
|
||||
if g.tcPosX < 1 {
|
||||
if g.tcPosY == 1 && rl.tabCompletionReverse {
|
||||
g.tcPosX = 1
|
||||
g.tcPosY = 0
|
||||
} else {
|
||||
// This is when multiple ligns, not yet on first one.
|
||||
g.tcPosX = g.tcMaxX
|
||||
g.tcPosY--
|
||||
}
|
||||
}
|
||||
if g.tcPosY > g.tcMaxY {
|
||||
g.tcPosY = 1
|
||||
return true, true
|
||||
}
|
||||
|
||||
// If we must move to next line in same group
|
||||
if g.tcPosX > g.tcMaxX {
|
||||
g.tcPosX = 1
|
||||
g.tcPosY++
|
||||
}
|
||||
|
||||
// Real max number of suggestions.
|
||||
max := g.tcMaxX * g.tcMaxY
|
||||
if max > len(g.Suggestions) {
|
||||
max = len(g.Suggestions)
|
||||
}
|
||||
|
||||
// We arrived at the end of suggestions. This condition can never be triggered
|
||||
// while going in the reverse order, only forward, so no further checks in it.
|
||||
if (g.tcMaxX*(g.tcPosY-1))+g.tcPosX > max {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// In case we are reverse cycling and currently selecting the first item,
|
||||
// we adjust the coordinates to point to the last item and return
|
||||
// We set g.tcPosY because the printer needs to get the a candidate nonetheless.
|
||||
if rl.tabCompletionReverse && g.tcPosX == 1 && g.tcPosY == 0 {
|
||||
g.tcPosY = 1
|
||||
return true, false
|
||||
}
|
||||
|
||||
// By default, come back to this group for next item.
|
||||
return false, false
|
||||
}
|
||||
|
||||
// writeGrid - A grid completion string
|
||||
func (g *CompletionGroup) writeGrid(rl *Readline) (comp string) {
|
||||
|
||||
// If group title, print it and adjust offset.
|
||||
if g.Name != "" {
|
||||
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET)
|
||||
rl.tcUsedY++
|
||||
}
|
||||
|
||||
cellWidth := strconv.Itoa((GetTermWidth() / g.tcMaxX) - 4)
|
||||
x := 0
|
||||
y := 1
|
||||
|
||||
for i := range g.Suggestions {
|
||||
x++
|
||||
if x > g.tcMaxX {
|
||||
x = 1
|
||||
y++
|
||||
if y > g.tcMaxY {
|
||||
y--
|
||||
break
|
||||
} else {
|
||||
comp += "\r\n"
|
||||
}
|
||||
}
|
||||
|
||||
if (x == g.tcPosX && y == g.tcPosY) && (g.isCurrent) {
|
||||
comp += seqInvert
|
||||
}
|
||||
|
||||
sugg := g.Suggestions[i]
|
||||
if len(sugg) > GetTermWidth() {
|
||||
sugg = sugg[:GetTermWidth()-4] + "..."
|
||||
}
|
||||
formatStr := "%-" + cellWidth + "s%s "
|
||||
if g.tcMaxX == 1 {
|
||||
formatStr = "%s%s"
|
||||
}
|
||||
comp += fmt.Sprintf(formatStr, fmtEscape(sugg), seqReset)
|
||||
}
|
||||
|
||||
// Always add a newline to the group if the end if not punctuated with one
|
||||
if !strings.HasSuffix(comp, "\n") {
|
||||
comp += "\n"
|
||||
}
|
||||
|
||||
// Add the equivalent of this group's size to final screen clearing.
|
||||
// This is either the max allowed print size for this group, or its actual size if inferior.
|
||||
if g.MaxLength < y {
|
||||
rl.tcUsedY += g.MaxLength
|
||||
} else {
|
||||
rl.tcUsedY += y
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
293
golibs/readline/comp-group.go
Normal file
@ -0,0 +1,293 @@
|
||||
package readline
|
||||
|
||||
import "strings"
|
||||
|
||||
// CompletionGroup - A group/category of items offered to completion, with its own
|
||||
// name, descriptions and completion display format/type.
|
||||
// The output, if there are multiple groups available for a given completion input,
|
||||
// will look like ZSH's completion system.
|
||||
type CompletionGroup struct {
|
||||
Name string // If not nil, printed on top of the group's completions
|
||||
Description string
|
||||
|
||||
// Candidates & related
|
||||
Suggestions []string
|
||||
Aliases map[string]string // A candidate has an alternative name (ex: --long, -l option flags)
|
||||
Descriptions map[string]string // Items descriptions
|
||||
ItemDisplays map[string]string // What to display the item as (can be used for styling items)
|
||||
DisplayType TabDisplayType // Map, list or normal
|
||||
MaxLength int // Each group can be limited in the number of comps offered
|
||||
|
||||
// When this is true, the completion is inserted really (not virtually) without
|
||||
// the trailing slash, if any. This is used when we want to complete paths.
|
||||
TrimSlash bool
|
||||
// PathSeparator - If you intend to write path completions, you can specify the path separator to use, depending on which OS you want completion for. By default, this will be set to the GOOS of the binary. This is also used internally for many things.
|
||||
PathSeparator rune
|
||||
|
||||
// When this is true, we don't add a space after entering the candidate.
|
||||
// Can be used for multi-stage completions, like URLS (scheme:// + host)
|
||||
NoSpace bool
|
||||
|
||||
// For each group, we can define the min and max tab item length
|
||||
MinTabItemLength int
|
||||
MaxTabItemLength int
|
||||
|
||||
// Values used by the shell
|
||||
tcPosX int
|
||||
tcPosY int
|
||||
tcMaxX int
|
||||
tcMaxY int
|
||||
tcOffset int
|
||||
tcMaxLength int // Used when display is map/list, for determining message width
|
||||
tcMaxLengthAlt int // Same as tcMaxLength but for SuggestionsAlt.
|
||||
|
||||
// true if we want to cycle through suggestions because they overflow MaxLength
|
||||
allowCycle bool
|
||||
|
||||
// This is to say we are currently cycling through this group, for highlighting choice
|
||||
isCurrent bool
|
||||
}
|
||||
|
||||
// init - The completion group computes and sets all its values, and is then ready to work.
|
||||
func (g *CompletionGroup) init(rl *Readline) {
|
||||
|
||||
// Details common to all displays
|
||||
g.checkCycle(rl) // Based on the number of groups given to the shell, allows cycling or not
|
||||
g.checkMaxLength(rl)
|
||||
|
||||
// Details specific to tab display modes
|
||||
switch g.DisplayType {
|
||||
|
||||
case TabDisplayGrid:
|
||||
g.initGrid(rl)
|
||||
case TabDisplayMap:
|
||||
g.initMap(rl)
|
||||
case TabDisplayList:
|
||||
g.initList(rl)
|
||||
}
|
||||
}
|
||||
|
||||
// updateTabFind - When searching through all completion groups (whether it be command history or not),
|
||||
// we ask each of them to filter its own items and return the results to the shell for aggregating them.
|
||||
// The rx parameter is passed, as the shell already checked that the search pattern is valid.
|
||||
func (g *CompletionGroup) updateTabFind(rl *Readline) {
|
||||
|
||||
suggs := rl.Searcher(rl.search, g.Suggestions)
|
||||
// We perform filter right here, so we create a new completion group, and populate it with our results.
|
||||
/*for i := range g.Suggestions {
|
||||
if rl.regexSearch == nil { continue }
|
||||
if rl.regexSearch.MatchString(g.Suggestions[i]) {
|
||||
suggs = append(suggs, g.Suggestions[i])
|
||||
} else if g.DisplayType == TabDisplayList && rl.regexSearch.MatchString(g.Descriptions[g.Suggestions[i]]) {
|
||||
// this is a list so lets also check the descriptions
|
||||
suggs = append(suggs, g.Suggestions[i])
|
||||
}
|
||||
}*/
|
||||
|
||||
// We overwrite the group's items, (will be refreshed as soon as something is typed in the search)
|
||||
g.Suggestions = suggs
|
||||
|
||||
// Finally, the group computes its new printing settings
|
||||
g.init(rl)
|
||||
|
||||
// If we are in history completion, we directly pass to the first candidate
|
||||
if rl.modeAutoFind && rl.searchMode == HistoryFind && len(g.Suggestions) > 0 {
|
||||
g.tcPosY = 1
|
||||
}
|
||||
}
|
||||
|
||||
// checkCycle - Based on the number of groups given to the shell, allows cycling or not
|
||||
func (g *CompletionGroup) checkCycle(rl *Readline) {
|
||||
if len(rl.tcGroups) == 1 {
|
||||
g.allowCycle = true
|
||||
}
|
||||
if len(rl.tcGroups) >= 10 {
|
||||
g.allowCycle = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// checkMaxLength - Based on the number of groups given to the shell, check/set MaxLength defaults
|
||||
func (g *CompletionGroup) checkMaxLength(rl *Readline) {
|
||||
|
||||
// This means the user forgot to set it
|
||||
if g.MaxLength == 0 {
|
||||
if len(rl.tcGroups) < 5 {
|
||||
g.MaxLength = 20
|
||||
}
|
||||
|
||||
if len(rl.tcGroups) >= 5 {
|
||||
g.MaxLength = 20
|
||||
}
|
||||
|
||||
// Lists that have a alternative completions are not allowed to have
|
||||
// MaxLength set, because rolling does not work yet.
|
||||
if g.DisplayType == TabDisplayList {
|
||||
g.MaxLength = 1000 // Should be enough not to trigger anything related.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// checkNilItems - For each completion group we avoid nil maps and possibly other items
|
||||
func checkNilItems(groups []*CompletionGroup) (checked []*CompletionGroup) {
|
||||
|
||||
for _, grp := range groups {
|
||||
if grp.Descriptions == nil || len(grp.Descriptions) == 0 {
|
||||
grp.Descriptions = make(map[string]string)
|
||||
}
|
||||
if grp.Aliases == nil || len(grp.Aliases) == 0 {
|
||||
grp.Aliases = make(map[string]string)
|
||||
}
|
||||
checked = append(checked, grp)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// writeCompletion - This function produces a formatted string containing all appropriate items
|
||||
// and according to display settings. This string is then appended to the main completion string.
|
||||
func (g *CompletionGroup) writeCompletion(rl *Readline) (comp string) {
|
||||
|
||||
// Avoids empty groups in suggestions
|
||||
if len(g.Suggestions) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Depending on display type we produce the approriate string
|
||||
switch g.DisplayType {
|
||||
|
||||
case TabDisplayGrid:
|
||||
comp += g.writeGrid(rl)
|
||||
case TabDisplayMap:
|
||||
comp += g.writeMap(rl)
|
||||
case TabDisplayList:
|
||||
comp += g.writeList(rl)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getCurrentCell - The completion groups computes the current cell value,
|
||||
// depending on its display type and its different parameters
|
||||
func (g *CompletionGroup) getCurrentCell(rl *Readline) string {
|
||||
|
||||
switch g.DisplayType {
|
||||
case TabDisplayGrid:
|
||||
// x & y coodinates + safety check
|
||||
cell := (g.tcMaxX * (g.tcPosY - 1)) + g.tcOffset + g.tcPosX - 1
|
||||
if cell < 0 {
|
||||
cell = 0
|
||||
}
|
||||
|
||||
if cell < len(g.Suggestions) {
|
||||
return g.Suggestions[cell]
|
||||
}
|
||||
return ""
|
||||
|
||||
case TabDisplayMap:
|
||||
// x & y coodinates + safety check
|
||||
cell := g.tcOffset + g.tcPosY - 1
|
||||
if cell < 0 {
|
||||
cell = 0
|
||||
}
|
||||
|
||||
sugg := g.Suggestions[cell]
|
||||
return sugg
|
||||
|
||||
case TabDisplayList:
|
||||
// x & y coodinates + safety check
|
||||
cell := g.tcOffset + g.tcPosY - 1
|
||||
if cell < 0 {
|
||||
cell = 0
|
||||
}
|
||||
|
||||
sugg := g.Suggestions[cell]
|
||||
|
||||
// If we are in the alt suggestions column, check key and return
|
||||
if g.tcPosX == 1 {
|
||||
if alt, ok := g.Aliases[sugg]; ok {
|
||||
return alt
|
||||
}
|
||||
return sugg
|
||||
}
|
||||
return sugg
|
||||
}
|
||||
|
||||
// We should never get here
|
||||
return ""
|
||||
}
|
||||
|
||||
func (g *CompletionGroup) goFirstCell() {
|
||||
switch g.DisplayType {
|
||||
case TabDisplayGrid:
|
||||
g.tcPosX = 1
|
||||
g.tcPosY = 1
|
||||
|
||||
case TabDisplayList:
|
||||
g.tcPosX = 0
|
||||
g.tcPosY = 1
|
||||
g.tcOffset = 0
|
||||
|
||||
case TabDisplayMap:
|
||||
g.tcPosX = 0
|
||||
g.tcPosY = 1
|
||||
g.tcOffset = 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (g *CompletionGroup) goLastCell() {
|
||||
switch g.DisplayType {
|
||||
case TabDisplayGrid:
|
||||
g.tcPosY = g.tcMaxY
|
||||
|
||||
restX := len(g.Suggestions) % g.tcMaxX
|
||||
if restX != 0 {
|
||||
g.tcPosX = restX
|
||||
} else {
|
||||
g.tcPosX = g.tcMaxX
|
||||
}
|
||||
|
||||
// We need to adjust the X position depending
|
||||
// on the interpretation of the remainder with
|
||||
// respect to the group's MaxLength.
|
||||
restY := len(g.Suggestions) % g.tcMaxY
|
||||
maxY := len(g.Suggestions) / g.tcMaxX
|
||||
if restY == 0 && maxY > g.MaxLength {
|
||||
g.tcPosX = g.tcMaxX
|
||||
}
|
||||
if restY != 0 && maxY > g.MaxLength-1 {
|
||||
g.tcPosX = g.tcMaxX
|
||||
}
|
||||
|
||||
case TabDisplayList:
|
||||
// By default, the last item is at maxY
|
||||
g.tcPosY = g.tcMaxY
|
||||
|
||||
// If the max length is smaller than the number
|
||||
// of suggestions, we need to adjust the offset.
|
||||
if len(g.Suggestions) > g.MaxLength {
|
||||
g.tcOffset = len(g.Suggestions) - g.tcMaxY
|
||||
}
|
||||
|
||||
// We do not take into account the alternative suggestions
|
||||
g.tcPosX = 0
|
||||
|
||||
case TabDisplayMap:
|
||||
// By default, the last item is at maxY
|
||||
g.tcPosY = g.tcMaxY
|
||||
|
||||
// If the max length is smaller than the number
|
||||
// of suggestions, we need to adjust the offset.
|
||||
if len(g.Suggestions) > g.MaxLength {
|
||||
g.tcOffset = len(g.Suggestions) - g.tcMaxY
|
||||
}
|
||||
|
||||
// We do not take into account the alternative suggestions
|
||||
g.tcPosX = 0
|
||||
}
|
||||
}
|
||||
|
||||
func fmtEscape(s string) string {
|
||||
return strings.Replace(s, "%", "%%", -1)
|
||||
}
|
||||
264
golibs/readline/comp-list.go
Normal file
@ -0,0 +1,264 @@
|
||||
package readline
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// initList - List display details. Because of the way alternative completions
|
||||
// are handled, MaxLength cannot be set when there are alternative completions.
|
||||
func (g *CompletionGroup) initList(rl *Readline) {
|
||||
|
||||
// We may only ever have two different
|
||||
// columns: (suggestions, and alternatives)
|
||||
g.tcMaxX = 2
|
||||
|
||||
// We make the list anyway, especially if we need to use it later
|
||||
if g.Descriptions == nil {
|
||||
g.Descriptions = make(map[string]string)
|
||||
}
|
||||
if g.Aliases == nil {
|
||||
g.Aliases = make(map[string]string)
|
||||
}
|
||||
|
||||
// Compute size of each completion item box. Group independent
|
||||
g.tcMaxLength = rl.getListPad()
|
||||
|
||||
// Same for suggestions alt
|
||||
g.tcMaxLengthAlt = 0
|
||||
for i := range g.Suggestions {
|
||||
if len(g.Suggestions[i]) > g.tcMaxLength {
|
||||
g.tcMaxLength = len([]rune(g.Suggestions[i]))
|
||||
}
|
||||
}
|
||||
|
||||
// Max values depend on if we have alternative suggestions
|
||||
if len(g.Aliases) == 0 {
|
||||
g.tcMaxX = 1
|
||||
} else {
|
||||
g.tcMaxX = 2
|
||||
}
|
||||
|
||||
if len(g.Suggestions) > g.MaxLength {
|
||||
g.tcMaxY = g.MaxLength
|
||||
} else {
|
||||
g.tcMaxY = len(g.Suggestions)
|
||||
}
|
||||
|
||||
g.tcPosX = 0
|
||||
g.tcPosY = 0
|
||||
g.tcOffset = 0
|
||||
}
|
||||
|
||||
// moveTabListHighlight - Moves the highlighting for currently selected completion item (list display)
|
||||
// We don't care about the x, because only can have 2 columns of selectable choices (--long and -s)
|
||||
func (g *CompletionGroup) moveTabListHighlight(rl *Readline, x, y int) (done bool, next bool) {
|
||||
|
||||
// We dont' pass to x, because not managed by callers
|
||||
g.tcPosY += x
|
||||
g.tcPosY += y
|
||||
|
||||
// Lines
|
||||
if g.tcPosY < 1 {
|
||||
if rl.tabCompletionReverse {
|
||||
if g.tcOffset > 0 {
|
||||
g.tcPosY = 1
|
||||
g.tcOffset--
|
||||
} else {
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
}
|
||||
if g.tcPosY > g.tcMaxY {
|
||||
g.tcPosY--
|
||||
g.tcOffset++
|
||||
}
|
||||
|
||||
// Once we get to the end of choices: check which column we were selecting.
|
||||
if g.tcOffset+g.tcPosY > len(g.Suggestions) {
|
||||
// If we have alternative options and that we are not yet
|
||||
// completing them, start on top of their column
|
||||
if g.tcPosX == 0 && len(g.Aliases) > 0 {
|
||||
g.tcPosX++
|
||||
g.tcPosY = 1
|
||||
g.tcOffset = 0
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Else no alternatives, return for next group.
|
||||
// Reset all values, in case we pass on them again.
|
||||
g.tcPosX = 0 // First column
|
||||
g.tcPosY = 1 // first row
|
||||
g.tcOffset = 0
|
||||
return true, true
|
||||
}
|
||||
|
||||
// Here we must check, in x == 1, that the current choice
|
||||
// is not empty. Handle for both reverse and forward movements.
|
||||
sugg := g.Suggestions[g.tcPosY-1]
|
||||
_, ok := g.Aliases[sugg]
|
||||
if !ok && g.tcPosX == 1 {
|
||||
if rl.tabCompletionReverse {
|
||||
for i := len(g.Suggestions[:g.tcPosY-1]); i > 0; i-- {
|
||||
su := g.Suggestions[i]
|
||||
if _, ok := g.Aliases[su]; ok {
|
||||
g.tcPosY -= (len(g.Suggestions[:g.tcPosY-1])) - i
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
g.tcPosX = 0
|
||||
g.tcPosY = g.tcMaxY
|
||||
|
||||
} else {
|
||||
for i, su := range g.Suggestions[g.tcPosY-1:] {
|
||||
if _, ok := g.Aliases[su]; ok {
|
||||
g.tcPosY += i
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup offset if needs to be.
|
||||
// TODO: should be rewrited to conditionally process rolling menus with alternatives
|
||||
if g.tcOffset+g.tcPosY < 1 && len(g.Suggestions) > 0 {
|
||||
g.tcPosY = g.tcMaxY
|
||||
g.tcOffset = len(g.Suggestions) - g.tcMaxY
|
||||
}
|
||||
if g.tcOffset < 0 {
|
||||
g.tcOffset = 0
|
||||
}
|
||||
|
||||
// MIGHT BE NEEDED IF PROBLEMS WIHT ROLLING COMPLETIONS
|
||||
// ------------------------------------------------------------------------------
|
||||
// Once we get to the end of choices: check which column we were selecting.
|
||||
// We use +1 because we may have a single suggestion, and we just want "a ratio"
|
||||
// if g.tcOffset+g.tcPosY > len(g.Suggestions) {
|
||||
//
|
||||
// // If we have alternative options and that we are not yet
|
||||
// // completing them, start on top of their column
|
||||
// if g.tcPosX == 1 && len(g.SuggestionsAlt) > 0 {
|
||||
// g.tcPosX++
|
||||
// g.tcPosY = 1
|
||||
// g.tcOffset = 0
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// // Else no alternatives, return for next group.
|
||||
// g.tcPosY = 1
|
||||
// return true
|
||||
// }
|
||||
return false, false
|
||||
}
|
||||
|
||||
// writeList - A list completion string
|
||||
func (g *CompletionGroup) writeList(rl *Readline) (comp string) {
|
||||
|
||||
// Print group title and adjust offset if there is one.
|
||||
if g.Name != "" {
|
||||
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, g.Name, RESET)
|
||||
rl.tcUsedY++
|
||||
}
|
||||
|
||||
termWidth := GetTermWidth()
|
||||
if termWidth < 20 {
|
||||
// terminal too small. Probably better we do nothing instead of crash
|
||||
// We are more conservative than lmorg, and push it to 20 instead of 10
|
||||
return
|
||||
}
|
||||
|
||||
// Suggestion cells dimensions
|
||||
maxLength := g.tcMaxLength
|
||||
if maxLength > termWidth-9 {
|
||||
maxLength = termWidth - 9
|
||||
}
|
||||
cellWidth := strconv.Itoa(maxLength)
|
||||
|
||||
// Alternative suggestion cells dimensions
|
||||
maxLengthAlt := g.tcMaxLengthAlt + 2
|
||||
if maxLengthAlt > termWidth-9 {
|
||||
maxLengthAlt = termWidth - 9
|
||||
}
|
||||
cellWidthAlt := strconv.Itoa(maxLengthAlt)
|
||||
|
||||
// Descriptions cells dimensions
|
||||
maxDescWidth := termWidth - maxLength - maxLengthAlt - 4
|
||||
|
||||
// function highlights the cell depending on current selector place.
|
||||
highlight := func(y int, x int) string {
|
||||
if y == g.tcPosY && x == g.tcPosX && g.isCurrent {
|
||||
return seqInvert
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// For each line in completions
|
||||
y := 0
|
||||
for i := g.tcOffset; i < len(g.Suggestions); i++ {
|
||||
y++ // Consider next item
|
||||
if y > g.tcMaxY {
|
||||
break
|
||||
}
|
||||
|
||||
// Main suggestion
|
||||
item := g.Suggestions[i]
|
||||
if len(item) > maxLength {
|
||||
item = item[:maxLength-3] + "..."
|
||||
}
|
||||
sugg := fmt.Sprintf("\r%s%-"+cellWidth+"s", highlight(y, 0), fmtEscape(item))
|
||||
|
||||
// Alt suggestion
|
||||
alt, ok := g.Aliases[item]
|
||||
if ok {
|
||||
alt = fmt.Sprintf(" %s%"+cellWidthAlt+"s", highlight(y, 1), fmtEscape(alt))
|
||||
} else {
|
||||
// Else, make an empty cell
|
||||
alt = strings.Repeat(" ", maxLengthAlt+1) // + 2 to keep account of spaces
|
||||
}
|
||||
|
||||
styledSugg, ok := g.ItemDisplays[item]
|
||||
if ok {
|
||||
sugg = fmt.Sprintf("\r%s%-"+cellWidth+"s", highlight(y, 0), fmtEscape(styledSugg))
|
||||
}
|
||||
|
||||
// Description
|
||||
description := g.Descriptions[g.Suggestions[i]]
|
||||
if len(description) > maxDescWidth {
|
||||
description = description[:maxDescWidth-3] + "..." + RESET + "\n"
|
||||
} else {
|
||||
description += "\n"
|
||||
}
|
||||
|
||||
// Total completion line
|
||||
comp += sugg + seqReset + alt + " " + seqReset + description
|
||||
}
|
||||
|
||||
// Add the equivalent of this group's size to final screen clearing
|
||||
// Can be set and used only if no alterative completions have been given.
|
||||
if len(g.Aliases) == 0 {
|
||||
if len(g.Suggestions) > g.MaxLength {
|
||||
rl.tcUsedY += g.MaxLength
|
||||
} else {
|
||||
rl.tcUsedY += len(g.Suggestions)
|
||||
}
|
||||
} else {
|
||||
rl.tcUsedY += len(g.Suggestions)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (rl *Readline) getListPad() (pad int) {
|
||||
for _, group := range rl.tcGroups {
|
||||
if group.DisplayType == TabDisplayList {
|
||||
for i := range group.Suggestions {
|
||||
if len(group.Suggestions[i]) > pad {
|
||||
pad = len([]rune(group.Suggestions[i]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
140
golibs/readline/comp-map.go
Normal file
@ -0,0 +1,140 @@
|
||||
package readline
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// initMap - Map display details. Called each time we want to be sure to have
|
||||
// a working completion group either immediately, or later on. Generally defered.
|
||||
func (g *CompletionGroup) initMap(rl *Readline) {
|
||||
|
||||
// We make the map anyway, especially if we need to use it later
|
||||
if g.Descriptions == nil {
|
||||
g.Descriptions = make(map[string]string)
|
||||
}
|
||||
|
||||
// Compute size of each completion item box. Group independent
|
||||
g.tcMaxLength = 1
|
||||
for i := range g.Suggestions {
|
||||
if len(g.Descriptions[g.Suggestions[i]]) > g.tcMaxLength {
|
||||
g.tcMaxLength = len(g.Descriptions[g.Suggestions[i]])
|
||||
}
|
||||
}
|
||||
|
||||
g.tcPosX = 0
|
||||
g.tcPosY = 0
|
||||
g.tcOffset = 0
|
||||
|
||||
// Number of lines allowed to be printed for group
|
||||
if len(g.Suggestions) > g.MaxLength {
|
||||
g.tcMaxY = g.MaxLength
|
||||
} else {
|
||||
g.tcMaxY = len(g.Suggestions)
|
||||
}
|
||||
}
|
||||
|
||||
// moveTabMapHighlight - Moves the highlighting for currently selected completion item (map display)
|
||||
func (g *CompletionGroup) moveTabMapHighlight(rl *Readline, x, y int) (done bool, next bool) {
|
||||
|
||||
g.tcPosY += x
|
||||
g.tcPosY += y
|
||||
|
||||
// Lines
|
||||
if g.tcPosY < 1 {
|
||||
if rl.tabCompletionReverse {
|
||||
if g.tcOffset > 0 {
|
||||
g.tcPosY = 1
|
||||
g.tcOffset--
|
||||
} else {
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
}
|
||||
if g.tcPosY > g.tcMaxY {
|
||||
g.tcPosY--
|
||||
g.tcOffset++
|
||||
}
|
||||
|
||||
if g.tcOffset+g.tcPosY < 1 && len(g.Suggestions) > 0 {
|
||||
g.tcPosY = g.tcMaxY
|
||||
g.tcOffset = len(g.Suggestions) - g.tcMaxY
|
||||
}
|
||||
if g.tcOffset < 0 {
|
||||
g.tcOffset = 0
|
||||
}
|
||||
|
||||
if g.tcOffset+g.tcPosY > len(g.Suggestions) {
|
||||
g.tcOffset--
|
||||
return true, true
|
||||
}
|
||||
return false, false
|
||||
}
|
||||
|
||||
// writeMap - A map or list completion string
|
||||
func (g *CompletionGroup) writeMap(rl *Readline) (comp string) {
|
||||
|
||||
if g.Name != "" {
|
||||
// Print group title (changes with line returns depending on type)
|
||||
comp += fmt.Sprintf("%s%s%s %s\n", BOLD, YELLOW, fmtEscape(g.Name), RESET)
|
||||
rl.tcUsedY++
|
||||
}
|
||||
|
||||
termWidth := GetTermWidth()
|
||||
if termWidth < 20 {
|
||||
// terminal too small. Probably better we do nothing instead of crash
|
||||
// We are more conservative than lmorg, and push it to 20 instead of 10
|
||||
return
|
||||
}
|
||||
|
||||
// Set all necessary dimensions
|
||||
maxLength := g.tcMaxLength
|
||||
if maxLength > termWidth-9 {
|
||||
maxLength = termWidth - 9
|
||||
}
|
||||
maxDescWidth := termWidth - maxLength - 4
|
||||
|
||||
cellWidth := strconv.Itoa(maxLength)
|
||||
itemWidth := strconv.Itoa(maxDescWidth)
|
||||
y := 0
|
||||
|
||||
// Highlighting function
|
||||
highlight := func(y int) string {
|
||||
if y == g.tcPosY && g.isCurrent {
|
||||
return seqInvert
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// String formating
|
||||
var item, description string
|
||||
for i := g.tcOffset; i < len(g.Suggestions); i++ {
|
||||
y++ // Consider new item
|
||||
if y > g.tcMaxY {
|
||||
break
|
||||
}
|
||||
|
||||
item = g.Suggestions[i]
|
||||
|
||||
if len(item) > maxDescWidth {
|
||||
item = item[:maxDescWidth-3] + "..."
|
||||
}
|
||||
|
||||
description = g.Descriptions[g.Suggestions[i]]
|
||||
if len(description) > maxLength {
|
||||
description = description[:maxLength-3] + "..."
|
||||
}
|
||||
|
||||
comp += fmt.Sprintf("\r%-"+cellWidth+"s %s %-"+itemWidth+"s %s\n",
|
||||
description, highlight(y), fmtEscape(item), seqReset)
|
||||
}
|
||||
|
||||
// Add the equivalent of this group's size to final screen clearing
|
||||
if len(g.Suggestions) > g.MaxLength {
|
||||
rl.tcUsedY += g.MaxLength
|
||||
} else {
|
||||
rl.tcUsedY += len(g.Suggestions)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
180
golibs/readline/cursor.go
Normal file
@ -0,0 +1,180 @@
|
||||
package readline
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Lmorg code
|
||||
// -------------------------------------------------------------------------------
|
||||
|
||||
func leftMost() []byte {
|
||||
fd := int(os.Stdout.Fd())
|
||||
w, _, err := GetSize(fd)
|
||||
if err != nil {
|
||||
return []byte{'\r', '\n'}
|
||||
}
|
||||
|
||||
b := make([]byte, w+1)
|
||||
for i := 0; i < w; i++ {
|
||||
b[i] = ' '
|
||||
}
|
||||
b[w] = '\r'
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
var rxRcvCursorPos = regexp.MustCompile("^\x1b([0-9]+);([0-9]+)R$")
|
||||
|
||||
func (rl *Readline) getCursorPos() (x int, y int) {
|
||||
if !rl.EnableGetCursorPos {
|
||||
return -1, -1
|
||||
}
|
||||
|
||||
disable := func() (int, int) {
|
||||
os.Stderr.WriteString("\r\ngetCursorPos() not supported by terminal emulator, disabling....\r\n")
|
||||
rl.EnableGetCursorPos = false
|
||||
return -1, -1
|
||||
}
|
||||
|
||||
print(seqGetCursorPos)
|
||||
b := make([]byte, 64)
|
||||
i, err := os.Stdin.Read(b)
|
||||
if err != nil {
|
||||
return disable()
|
||||
}
|
||||
|
||||
if !rxRcvCursorPos.Match(b[:i]) {
|
||||
return disable()
|
||||
}
|
||||
|
||||
match := rxRcvCursorPos.FindAllStringSubmatch(string(b[:i]), 1)
|
||||
y, err = strconv.Atoi(match[0][1])
|
||||
if err != nil {
|
||||
return disable()
|
||||
}
|
||||
|
||||
x, err = strconv.Atoi(match[0][2])
|
||||
if err != nil {
|
||||
return disable()
|
||||
}
|
||||
|
||||
return x, y
|
||||
}
|
||||
|
||||
// DISPLAY ------------------------------------------------------------
|
||||
// All cursorMoveFunctions move the cursor as it is seen by the user.
|
||||
// This means that they are not used to keep any reference point when
|
||||
// when we internally move around clearning and printing things
|
||||
|
||||
/*
|
||||
func moveCursorUpBuffered(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(rl.bufferedOut, "\x1b[%dA", i)
|
||||
}
|
||||
|
||||
func moveCursorDownBuffered(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(rl.bufferedOut, "\x1b[%dB", i)
|
||||
}
|
||||
|
||||
func moveCursorForwardsBuffered(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(rl.bufferedOut, "\x1b[%dC", i)
|
||||
}
|
||||
|
||||
func moveCursorUpBuffered(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(rl.bufferedOut, "\x1b[%dD", i)
|
||||
}
|
||||
*/
|
||||
|
||||
func moveCursorUp(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
printf("\x1b[%dA", i)
|
||||
}
|
||||
|
||||
func moveCursorDown(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
printf("\x1b[%dB", i)
|
||||
}
|
||||
|
||||
func moveCursorForwards(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
printf("\x1b[%dC", i)
|
||||
}
|
||||
|
||||
func moveCursorBackwards(i int) {
|
||||
if i < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
printf("\x1b[%dD", i)
|
||||
}
|
||||
|
||||
func hideCursor() {
|
||||
print(seqHideCursor)
|
||||
}
|
||||
|
||||
func unhideCursor() {
|
||||
print(seqUnhideCursor)
|
||||
}
|
||||
|
||||
func (rl *Readline) backspace(forward bool) {
|
||||
if len(rl.line) == 0 || rl.pos == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
rl.deleteBackspace(forward)
|
||||
}
|
||||
|
||||
func (rl *Readline) moveCursorByAdjust(adjust int) {
|
||||
switch {
|
||||
case adjust > 0:
|
||||
rl.pos += adjust
|
||||
case adjust < 0:
|
||||
rl.pos += adjust
|
||||
}
|
||||
|
||||
// The position can never be negative
|
||||
if rl.pos < 0 {
|
||||
rl.pos = 0
|
||||
}
|
||||
|
||||
// The cursor can never be longer than the line
|
||||
if rl.pos > len(rl.line) {
|
||||
rl.pos = len(rl.line)
|
||||
}
|
||||
|
||||
// If we are at the end of line, and not in Insert mode, move back one.
|
||||
if rl.modeViMode != VimInsert && (rl.pos == len(rl.line)) && len(rl.line) > 0 {
|
||||
if rl.modeViMode != VimInsert {
|
||||
rl.pos--
|
||||
} else if rl.modeViMode == VimInsert && rl.searchMode == HistoryFind && rl.modeAutoFind {
|
||||
rl.pos--
|
||||
}
|
||||
}
|
||||
}
|
||||
79
golibs/readline/editor.go
Normal file
@ -0,0 +1,79 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package readline
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// writeTempFile - This function optionally accepts a filename (generally specified with an extension).
|
||||
func (rl *Readline) writeTempFile(content []byte, filename string) (string, error) {
|
||||
// The final path to the buffer on disk
|
||||
var path string
|
||||
|
||||
// If the user has not provided any filename (including an extension)
|
||||
// we generate a random filename with no extension.
|
||||
if filename == "" {
|
||||
fileID := strconv.Itoa(time.Now().Nanosecond()) + ":" + string(rl.line)
|
||||
|
||||
h := md5.New()
|
||||
_, err := h.Write([]byte(fileID))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
name := "readline-" + hex.EncodeToString(h.Sum(nil)) + "-" + strconv.Itoa(os.Getpid())
|
||||
path = filepath.Join(rl.TempDirectory, name)
|
||||
} else {
|
||||
// Else, still use the temp/ dir, but with the provided filename
|
||||
path = filepath.Join(rl.TempDirectory, filename)
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(content)
|
||||
return path, err
|
||||
}
|
||||
|
||||
func readTempFile(name string) ([]byte, error) {
|
||||
file, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b) > 0 && b[len(b)-1] == '\n' {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
|
||||
if len(b) > 0 && b[len(b)-1] == '\r' {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
|
||||
if len(b) > 0 && b[len(b)-1] == '\n' {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
|
||||
if len(b) > 0 && b[len(b)-1] == '\r' {
|
||||
b = b[:len(b)-1]
|
||||
}
|
||||
|
||||
err = os.Remove(name)
|
||||
return b, err
|
||||
}
|
||||
10
golibs/readline/editor_plan9.go
Normal file
@ -0,0 +1,10 @@
|
||||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package readline
|
||||
|
||||
import "errors"
|
||||
|
||||
func (rl *Readline) launchEditor(multiline []rune) ([]rune, error) {
|
||||
return rl.line, errors.New("Not currently supported on Plan 9")
|
||||
}
|
||||
46
golibs/readline/editor_unix.go
Normal file
@ -0,0 +1,46 @@
|
||||
//go:build !windows && !plan9
|
||||
|
||||
package readline
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
const defaultEditor = "vi"
|
||||
|
||||
// StartEditorWithBuffer - Enables a consumer of this console application to
|
||||
// open an arbitrary buffer into the system editor. Currently only implemnted
|
||||
// on *Nix systems. The modified buffer is returned when the editor quits, and
|
||||
// depending on the actions taken by the user within it (eg: x or q! in Vim)
|
||||
// The filename parameter can be used to pass a specific filename.ext pattern,
|
||||
// which might be useful if the editor has builtin filetype plugin functionality.
|
||||
func (rl *Readline) StartEditorWithBuffer(multiline []rune, filename string) ([]rune, error) {
|
||||
name, err := rl.writeTempFile([]byte(string(multiline)), filename)
|
||||
if err != nil {
|
||||
return multiline, err
|
||||
}
|
||||
|
||||
editor := os.Getenv("EDITOR")
|
||||
// default editor if $EDITOR not set
|
||||
if editor == "" {
|
||||
editor = defaultEditor
|
||||
}
|
||||
|
||||
cmd := exec.Command(editor, name)
|
||||
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return multiline, err
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
return multiline, err
|
||||
}
|
||||
|
||||
b, err := readTempFile(name)
|
||||
return []rune(string(b)), err
|
||||
}
|
||||
10
golibs/readline/editor_windows.go
Normal file
@ -0,0 +1,10 @@
|
||||
//go:build windows
|
||||
|
||||
package readline
|
||||
|
||||
import "errors"
|
||||
|
||||
// StartEditorWithBuffer - Not implemented on Windows platforms.
|
||||
func (rl *Readline) StartEditorWithBuffer(multiline []rune, filename string) ([]rune, error) {
|
||||
return rl.line, errors.New("Not currently supported on Windows")
|
||||
}
|
||||
24
golibs/readline/errors.go
Normal file
@ -0,0 +1,24 @@
|
||||
package readline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// ErrCtrlC is returned when ctrl+c is pressed.
|
||||
// WARNING: this is being deprecated! Please use CtrlC (type error) instead
|
||||
ErrCtrlC = "Ctrl+C"
|
||||
|
||||
// ErrEOF is returned when ctrl+d is pressed.
|
||||
// WARNING: this is being deprecated! Please use EOF (type error) instead
|
||||
ErrEOF = "EOF"
|
||||
)
|
||||
|
||||
var (
|
||||
// CtrlC is returned when ctrl+c is pressed
|
||||
CtrlC = errors.New("Ctrl+C")
|
||||
|
||||
// EOF is returned when ctrl+d is pressed.
|
||||
// (this is actually the same value as io.EOF)
|
||||
EOF = errors.New("EOF")
|
||||
)
|
||||
23
golibs/readline/events.go
Normal file
@ -0,0 +1,23 @@
|
||||
package readline
|
||||
|
||||
// EventReturn is a structure returned by the callback event function.
|
||||
// This is used by readline to determine what state the API should
|
||||
// return to after the readline event.
|
||||
type EventReturn struct {
|
||||
ForwardKey bool
|
||||
ClearHelpers bool
|
||||
CloseReadline bool
|
||||
InfoText []rune
|
||||
NewLine []rune
|
||||
NewPos int
|
||||
}
|
||||
|
||||
// AddEvent registers a new keypress handler
|
||||
func (rl *Readline) AddEvent(keyPress string, callback func(string, []rune, int) *EventReturn) {
|
||||
rl.evtKeyPress[keyPress] = callback
|
||||
}
|
||||
|
||||
// DelEvent deregisters an existing keypress handler
|
||||
func (rl *Readline) DelEvent(keyPress string) {
|
||||
delete(rl.evtKeyPress, keyPress)
|
||||
}
|
||||
12
golibs/readline/go.mod
Normal file
@ -0,0 +1,12 @@
|
||||
module github.com/maxlandon/readline
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/evilsocket/islazy v1.10.6
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0
|
||||
github.com/rivo/uniseg v0.2.0
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4
|
||||
)
|
||||
12
golibs/readline/go.sum
Normal file
@ -0,0 +1,12 @@
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/evilsocket/islazy v1.10.6 h1:MFq000a1ByoumoJWlytqg0qon0KlBeUfPsDjY0hK0bo=
|
||||
github.com/evilsocket/islazy v1.10.6/go.mod h1:OrwQGYg3DuZvXUfmH+KIZDjwTCbrjy48T24TUpGqVVw=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw=
|
||||
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||