mirror of https://github.com/sayanarijit/xplr
Compare commits
497 Commits
Author | SHA1 | Date |
---|---|---|
Pierre | 8c8732324e | 4 weeks ago |
Pierre | 9d802fa17c | 4 weeks ago |
Arijit Basu | 68fb6fa1a2 | 3 months ago |
Arijit Basu | a82ea6a3e5 | 3 months ago |
Arijit Basu | e13dd21728 | 3 months ago |
Arijit Basu | 182a201b0d | 5 months ago |
Arijit Basu | e0b0466e42 | 5 months ago |
Arijit Basu | 805e1594ed | 5 months ago |
Arijit Basu | 41648ced34 | 5 months ago |
Arijit Basu | 89d7bccce8 | 5 months ago |
Arijit Basu | e15c1e8a8c | 5 months ago |
Arijit Basu | 8afdf9e478 | 5 months ago |
Abhinav Natarajan | a48dae008c | 5 months ago |
Arijit Basu | ad8afa9d38 | 5 months ago |
Arijit Basu | c2a11059c8 | 5 months ago |
Arijit Basu | 6d7ccce282 | 5 months ago |
Arijit Basu | 90df0a2b5a | 5 months ago |
Arijit Basu | ce52bcdf94 | 5 months ago |
Arijit Basu | 6fb0781fe4 | 5 months ago |
Arijit Basu | c1bb251fef | 6 months ago |
Arijit Basu | 976530ba70 | 6 months ago |
Arijit Basu | 96da7e1da8 | 6 months ago |
Arijit Basu | 96ffe8680b | 6 months ago |
Ahmed ElSamhaa | 1600ad9a9c | 6 months ago |
Ahmed ElSamhaa | 2a3d056bf1 | 6 months ago |
Ahmed ElSamhaa | 91276f6871 | 6 months ago |
Ahmed ElSamhaa | 00bd54abe9 | 6 months ago |
Ahmed ElSamhaa | 95621af9eb | 6 months ago |
Ahmed ElSamhaa | 5240b3904b | 6 months ago |
Ahmed ElSamhaa | a6fb695ff9 | 6 months ago |
Ahmed ElSamhaa | fd40de26e7 | 6 months ago |
Ahmed ElSamhaa | 87805509c5 | 6 months ago |
Ahmed ElSamhaa | 4aa367ca7c | 6 months ago |
Ahmed ElSamhaa | 01606e0e60 | 6 months ago |
Ahmed ElSamhaa | e834242f5d | 6 months ago |
alice | 7c6dffc2c6 | 6 months ago |
har7an | d5217f6677 | 6 months ago |
Arijit Basu | 0285f0824c | 6 months ago |
Arijit Basu | a6b19425ae | 6 months ago |
Arijit Basu | 9db8b2cc19 | 6 months ago |
mikoloism | 68500f3a8e | 7 months ago |
Arijit Basu | ded2e108bf | 8 months ago |
Arijit Basu | 6e8f3da971 | 8 months ago |
Arijit Basu | d76a70fed4 | 8 months ago |
Arijit Basu | 16673963aa | 8 months ago |
Arijit Basu | b0ef9a5190 | 8 months ago |
Arijit Basu | b70337708c | 8 months ago |
Arijit Basu | 9127d15494 | 8 months ago |
Arijit Basu | 66d9f7e586 | 8 months ago |
Arijit Basu | eab47a9044 | 8 months ago |
Arijit Basu | a9e3752f56 | 8 months ago |
Arijit Basu | 470bea1265 | 8 months ago |
Arijit Basu | cc578aaf0a | 9 months ago |
Arijit Basu | 50e81853fe | 9 months ago |
Arijit Basu | 414b45e4fd | 9 months ago |
Arijit Basu | 75dabeb283 | 9 months ago |
Arijit Basu | 1629398adf | 10 months ago |
Arijit Basu | dd8bb74dd4 | 10 months ago |
Felix Yan | 1dc5eae8fc | 10 months ago |
Arijit Basu | 484b94a961 | 11 months ago |
Arijit Basu | 50d9d1c54b | 1 year ago |
Dugan Chen | c7c3d2d7f6 | 1 year ago |
Arijit Basu | 1441275860 | 1 year ago |
Arijit Basu | 8af1647c09 | 1 year ago |
Arijit Basu | 22b5fca8d9 | 1 year ago |
Arijit Basu | 4a3f18100d | 1 year ago |
Lewis Cook | 6df168f8c1 | 1 year ago |
Arijit Basu | eeb7b5d684 | 1 year ago |
Arijit Basu | 9a7ff5846d | 1 year ago |
Arijit Basu | 1b2226512f | 1 year ago |
Arijit Basu | 56472998f5 | 1 year ago |
Arijit Basu | bf7ae3f748 | 1 year ago |
Arijit Basu | 94ba22bbcc | 1 year ago |
Arijit Basu | 567a6201a8 | 1 year ago |
Arijit Basu | 54d6d19003 | 1 year ago |
Dugan Chen | 4aeb3dd7c8 | 1 year ago |
Dugan Chen | 5626422ba4 | 1 year ago |
Arijit Basu | 1941355128 | 1 year ago |
Arijit Basu | 2f78691333 | 1 year ago |
Arijit Basu | a2fbf759dd | 1 year ago |
Arijit Basu | bc7f3cbbcf | 1 year ago |
Arijit Basu | ad50342260 | 1 year ago |
Arijit Basu | 313c61db96 | 1 year ago |
Arijit Basu | 255517c2a9 | 1 year ago |
Arijit Basu | 9844ae1476 | 1 year ago |
Arijit Basu | d282032b3d | 1 year ago |
Arijit Basu | ba26752f6c | 1 year ago |
Arijit Basu | 0cc8723e8e | 1 year ago |
Arijit Basu | 2f3c2ea0e4 | 1 year ago |
Arijit Basu | 219ee68152 | 1 year ago |
Arijit Basu | 859d888bde | 1 year ago |
Arijit Basu | f84d9d5c6a | 1 year ago |
Arijit Basu | 3fcfb1dbef | 1 year ago |
Arijit Basu | 4c51f0affe | 1 year ago |
Arijit Basu | 9d1bd99fd4 | 1 year ago |
Arijit Basu | 8209988ba6 | 1 year ago |
Arijit Basu | 33c5aa9f14 | 1 year ago |
Arijit Basu | cae50e4bcf | 1 year ago |
har7an | 048b1c701a | 1 year ago |
Arijit Basu | 508f4b980b | 1 year ago |
Karim Lalani | 28c9e0e3a0 | 1 year ago |
Noah Mayr | 4ccd9796c4 | 1 year ago |
Solitude | 36a7f1dc17 | 1 year ago |
Arijit Basu | 2cc8e0c510 | 1 year ago |
Arijit Basu | 27bc1217b3 | 1 year ago |
Arijit Basu | ab90381fda | 1 year ago |
Arijit Basu | 2a775371f6 | 1 year ago |
Arijit Basu | 3bee8060c7 | 1 year ago |
Arijit Basu | 97e30e2a6f | 1 year ago |
Arijit Basu | 7c26c48e18 | 1 year ago |
Arijit Basu | 17269ab17f | 1 year ago |
Arijit Basu | 8aff0ba918 | 1 year ago |
Arijit Basu | 4228a71ed9 | 1 year ago |
Arijit Basu | 252a1f5c37 | 1 year ago |
Henrique Goulart | 4f0db1f3e3 | 1 year ago |
Kian-Meng Ang | 8cca2d3566 | 1 year ago |
Arijit Basu | b995be0089 | 2 years ago |
Arijit Basu | c79175764b | 2 years ago |
Arijit Basu | e0d683b13a | 2 years ago |
Arijit Basu | 59279b816d | 2 years ago |
Arijit Basu | 8c4f744bb1 | 2 years ago |
Arijit Basu | d80b1b4db8 | 2 years ago |
Arijit Basu | 5f07e6143f | 2 years ago |
Arijit Basu | 43c88b4873 | 2 years ago |
Arijit Basu | d52ccac8ba | 2 years ago |
Arijit Basu | 26d79bd799 | 2 years ago |
Arijit Basu | 553b4ed3d6 | 2 years ago |
Arijit Basu | e9fc643bd9 | 2 years ago |
Arijit Basu | 3afccf2a54 | 2 years ago |
Arijit Basu | 1d9d5f5145 | 2 years ago |
Emanuel | 0715e242ef | 2 years ago |
Emanuel | 006c655e3a | 2 years ago |
Emanuel | 105e770f58 | 2 years ago |
emanuel | d6e33e68e3 | 2 years ago |
BoolPurist | 078da205ca | 2 years ago |
BoolPurist | caa365b4a0 | 2 years ago |
emanuel | 4c4e7f41b4 | 2 years ago |
Arijit Basu | e6e701b371 | 2 years ago |
Arijit Basu | 0cd5a9163d | 2 years ago |
Arijit Basu | ac958c9532 | 2 years ago |
Arijit Basu | 7fbcd18bb4 | 2 years ago |
Arijit Basu | 1369fcea9a | 2 years ago |
Arijit Basu | d17489de8d | 2 years ago |
Arijit Basu | 57492b84c0 | 2 years ago |
Arijit Basu | d719700122 | 2 years ago |
Arijit Basu | 011d3d4a68 | 2 years ago |
Arijit Basu | efb4d605c5 | 2 years ago |
Arijit Basu | e559b96e31 | 2 years ago |
Arijit Basu | d4edf3302f | 2 years ago |
Arijit Basu | c382768f23 | 2 years ago |
Arijit Basu | 6558ba8092 | 2 years ago |
Arijit Basu | 91c87a3901 | 2 years ago |
Arijit Basu | 25798009ad | 2 years ago |
Arijit Basu | 104dc0534b | 2 years ago |
Arijit Basu | 919a492131 | 2 years ago |
Arijit Basu | 399fd183d4 | 2 years ago |
Arijit Basu | 053615b041 | 2 years ago |
Arijit Basu | 76224c42e1 | 2 years ago |
Arijit Basu | ca4f4f3f45 | 2 years ago |
Arijit Basu | a0c19025dd | 2 years ago |
Arijit Basu | 69db7fa9e6 | 2 years ago |
Arijit Basu | 555bc02e10 | 2 years ago |
Arijit Basu | bbb1026ff8 | 2 years ago |
Arijit Basu | aea17b415b | 2 years ago |
Arijit Basu | 7e70487511 | 2 years ago |
Arijit Basu | 15e7123aba | 2 years ago |
Arijit Basu | 133ba120af | 2 years ago |
Orhun Parmaksız | 77164ff98a | 2 years ago |
Arijit Basu | ea8a1fcd46 | 2 years ago |
Arijit Basu | 70cb745c9f | 2 years ago |
Arijit Basu | 15979e4974 | 2 years ago |
Arijit Basu | 49c4729047 | 2 years ago |
Arijit Basu | 01060ed025 | 2 years ago |
Arijit Basu | 74318435e8 | 2 years ago |
Arijit Basu | 553f6b9bc6 | 2 years ago |
Arijit Basu | 71bd2e2776 | 2 years ago |
Arijit Basu | 94154c56df | 2 years ago |
Arijit Basu | 5c4dec05ed | 2 years ago |
Arijit Basu | 289556f452 | 2 years ago |
Arijit Basu | eba9de5a44 | 2 years ago |
Arijit Basu | e3a5f3c044 | 2 years ago |
Arijit Basu | a62b72bf2a | 2 years ago |
Arijit Basu | 00ffd077aa | 2 years ago |
Arijit Basu | bafe15e25e | 2 years ago |
Arijit Basu | fc798aad97 | 2 years ago |
Arijit Basu | deb28fa14a | 2 years ago |
Arijit Basu | 723dd6ec2c | 2 years ago |
Arijit Basu | 4a34780eb4 | 2 years ago |
Arijit Basu | b05e702536 | 2 years ago |
Arijit Basu | baa8759d68 | 2 years ago |
Arijit Basu | 52c8e05a1e | 2 years ago |
Arijit Basu | 65eee2dc90 | 2 years ago |
Arijit Basu | 77852b435f | 2 years ago |
Arijit Basu | 060544a2b8 | 2 years ago |
Arijit Basu | e51818dfc1 | 2 years ago |
Arijit Basu | f8b16a7ddd | 2 years ago |
Arijit Basu | 85c4253782 | 2 years ago |
Arijit Basu | 1d829c36e6 | 2 years ago |
Arijit Basu | 7c730557f2 | 2 years ago |
Arijit Basu | 93e9b2b2ae | 2 years ago |
Arijit Basu | 3fb174cdc0 | 2 years ago |
Arijit Basu | 111a648818 | 2 years ago |
Arijit Basu | 1e820030a0 | 2 years ago |
Arijit Basu | bffe1d43ec | 2 years ago |
Arijit Basu | fbe6b2be10 | 2 years ago |
Arijit Basu | 88fe71779b | 2 years ago |
Arijit Basu | 91e3990df1 | 2 years ago |
Arijit Basu | 7b8f38df5b | 2 years ago |
Jeremy Cantrell | 2b5755aa8a | 2 years ago |
Arijit Basu | 895d55ca23 | 2 years ago |
Arijit Basu | 1d20039fae | 2 years ago |
Arijit Basu | c93202b649 | 2 years ago |
Arijit Basu | 89b3731b56 | 2 years ago |
Arijit Basu | ae714e9713 | 2 years ago |
Arijit Basu | a4f98f0b63 | 2 years ago |
Arijit Basu | bd478ff64b | 2 years ago |
Arijit Basu | 57483bef41 | 2 years ago |
Arijit Basu | 93bd53bbcb | 2 years ago |
Arijit Basu | 81e83365f2 | 2 years ago |
Arijit Basu | 58c572d77a | 2 years ago |
Arijit Basu | f2713d9549 | 2 years ago |
Arijit Basu | 12264722ef | 2 years ago |
Arijit Basu | d579832c69 | 2 years ago |
Arijit Basu | 4e9c056a10 | 2 years ago |
Arijit Basu | b4ba56aa72 | 2 years ago |
Arijit Basu | 096e520a2b | 2 years ago |
Arijit Basu | 5dcaca3816 | 2 years ago |
Arijit Basu | f10fad958d | 2 years ago |
Arijit Basu | 9f9a6f9d73 | 2 years ago |
Arijit Basu | d8ff70bcaa | 2 years ago |
Arijit Basu | 12d2020f01 | 2 years ago |
Matt_BSD | d6d7e70425 | 2 years ago |
Arijit Basu | 846e0c7f60 | 2 years ago |
Arijit Basu | c42c4dc69d | 2 years ago |
Arijit Basu | 673bcd21c0 | 2 years ago |
Arijit Basu | a9b1527e55 | 2 years ago |
Junker | 7f1f33b422 | 2 years ago |
Arijit Basu | 220d253be4 | 2 years ago |
Arijit Basu | 95f1f45375 | 2 years ago |
Arijit Basu | ed8fb9dc6c | 2 years ago |
Arijit Basu | bf354352d5 | 2 years ago |
Arijit Basu | bea0f277bc | 2 years ago |
Arijit Basu | 07d0fe2dee | 2 years ago |
Arijit Basu | 4c4d3843e9 | 2 years ago |
Arijit Basu | a2f246f61e | 2 years ago |
Arijit Basu | 27394dd177 | 2 years ago |
Arijit Basu | 88416d862e | 2 years ago |
Arijit Basu | 563e2d1475 | 2 years ago |
Arijit Basu | 5370cc2e8c | 2 years ago |
Arijit Basu | e5576e2990 | 2 years ago |
Arijit Basu | e04b0964ae | 2 years ago |
Arijit Basu | 21b8c3e807 | 2 years ago |
Arijit Basu | 744b68197d | 2 years ago |
Arijit Basu | 02b631bf83 | 2 years ago |
Arijit Basu | 61d3b1635e | 2 years ago |
Arijit Basu | d35a4c58cb | 2 years ago |
Arijit Basu | 47f7d51e46 | 2 years ago |
Arijit Basu | de856b7870 | 2 years ago |
Arijit Basu | 2f9992bf8b | 2 years ago |
Arijit Basu | 3c8f6e1b08 | 2 years ago |
Arijit Basu | 07b2374616 | 2 years ago |
Arijit Basu | 7163c8932c | 2 years ago |
Arijit Basu | c3b25bb82f | 2 years ago |
Arijit Basu | e655c245c5 | 2 years ago |
Arijit Basu | f324e976da | 2 years ago |
Arijit Basu | 95248ae6d1 | 2 years ago |
Arijit Basu | e2cf313ee2 | 2 years ago |
Arijit Basu | a28addbd0e | 2 years ago |
Arijit Basu | 33e537949f | 2 years ago |
Arijit Basu | 7c3721a46c | 2 years ago |
Arijit Basu | e3184c5f88 | 2 years ago |
Arijit Basu | 3eabbd4ed6 | 2 years ago |
Arijit Basu | fba1c2d0d6 | 2 years ago |
Arijit Basu | 1df0d2cc90 | 2 years ago |
Arijit Basu | e22ed3f12e | 2 years ago |
Arijit Basu | 87783872a5 | 2 years ago |
Arijit Basu | 2aee3743e1 | 2 years ago |
Arijit Basu | 3733dc3262 | 2 years ago |
Arijit Basu | 278642cc41 | 2 years ago |
Arijit Basu | a25659d76c | 2 years ago |
Arijit Basu | cb2851875c | 2 years ago |
Arijit Basu | dc8cb30eb3 | 2 years ago |
Arijit Basu | cf052d2691 | 2 years ago |
Arijit Basu | 6f626d1ba0 | 2 years ago |
Arijit Basu | de45123e26 | 2 years ago |
Tom van Dijk | 4fa90d715e | 2 years ago |
Tom van Dijk | 2194ddcc61 | 2 years ago |
Tom van Dijk | c40ffe1633 | 2 years ago |
Arijit Basu | 90e56deedc | 2 years ago |
Arijit Basu | 2c39f9499a | 2 years ago |
Arijit Basu | 3da8140bb1 | 2 years ago |
Arijit Basu | 071195fd61 | 2 years ago |
Arijit Basu | e006152066 | 2 years ago |
Arijit Basu | 2179be473f | 2 years ago |
Arijit Basu | cc5b996db9 | 2 years ago |
Arijit Basu | e634d7150f | 2 years ago |
Arijit Basu | fadfb3d70c | 2 years ago |
Arijit Basu | d9465f1a57 | 2 years ago |
Arijit Basu | 8e1d16b751 | 2 years ago |
Arijit Basu | 37e660e0a6 | 2 years ago |
Shunsuke Mie | 626a48cf88 | 2 years ago |
Shunsuke Mie | 57c29d47c5 | 2 years ago |
Arijit Basu | 09002d0e65 | 2 years ago |
Arijit Basu | 1fef30ce56 | 2 years ago |
Arijit Basu | 86f56cc051 | 2 years ago |
Arijit Basu | 5f598648a8 | 2 years ago |
Arijit Basu | d2390285fb | 2 years ago |
Arijit Basu | 21c8ea68bb | 2 years ago |
Arijit Basu | 1de737cefa | 2 years ago |
Arijit Basu | 5765698fb7 | 2 years ago |
Arijit Basu | a233bc9bfb | 2 years ago |
Arijit Basu | e045b3ac3a | 2 years ago |
Arijit Basu | 3cfed5aa6f | 2 years ago |
Arijit Basu | 0d12762c1d | 2 years ago |
Arijit Basu | a66d5e05d5 | 2 years ago |
Arijit Basu | 8f7d6c4c9e | 2 years ago |
Arijit Basu | a89b58d686 | 2 years ago |
Arijit Basu | 188a2a8751 | 2 years ago |
Matt_BSD | ed392a0755 | 2 years ago |
Matt_BSD | 9a7e8cc0f4 | 2 years ago |
Matt_BSD | fb54b29dec | 2 years ago |
Arijit Basu | 7f8e293bd0 | 2 years ago |
Arijit Basu | 3301e03880 | 2 years ago |
Arijit Basu | a136a469a9 | 2 years ago |
Arijit Basu | d596a839ea | 2 years ago |
Arijit Basu | 6eec0ff489 | 2 years ago |
Arijit Basu | 2bd4346156 | 2 years ago |
Arijit Basu | 6d9d324a7d | 2 years ago |
Arijit Basu | b7ee0fc2e1 | 2 years ago |
Arijit Basu | 68ae6f5265 | 2 years ago |
Arijit Basu | b0af9d7f69 | 2 years ago |
Arijit Basu | 171c93dbb1 | 2 years ago |
Arijit Basu | bbcd0be0c4 | 2 years ago |
Arijit Basu | 569d3559d6 | 2 years ago |
Arijit Basu | 0e380f980c | 2 years ago |
Arijit Basu | 6c775d72dc | 2 years ago |
Arijit Basu | f6364b5403 | 2 years ago |
Arijit Basu | 9b686baf50 | 2 years ago |
Arijit Basu | adc47b72c3 | 2 years ago |
Arijit Basu | 9673eba89c | 2 years ago |
Arijit Basu | ebfbe39668 | 3 years ago |
Arijit Basu | a8b0775f7e | 3 years ago |
Arijit Basu | 4c167bae7f | 3 years ago |
Arijit Basu | 536ee9b439 | 3 years ago |
Arijit Basu | 583f685627 | 3 years ago |
Arijit Basu | aca6127166 | 3 years ago |
Arijit Basu | 0472bc3e66 | 3 years ago |
Arijit Basu | 88b79643cb | 3 years ago |
Arijit Basu | 88d13e675d | 3 years ago |
Arijit Basu | 84ba95f8e3 | 3 years ago |
Arijit Basu | 60e17f3b87 | 3 years ago |
Arijit Basu | a1d0c222a1 | 3 years ago |
Arijit Basu | dfb4854f01 | 3 years ago |
Arijit Basu | bfd403c894 | 3 years ago |
Arijit Basu | 28a378e031 | 3 years ago |
Arijit Basu | 2f320fa8a7 | 3 years ago |
Arijit Basu | 048f89139b | 3 years ago |
Arijit Basu | 3d81a49cec | 3 years ago |
Arijit Basu | 67eca1ecdf | 3 years ago |
Arijit Basu | 8385b92661 | 3 years ago |
Arijit Basu | e30116cca2 | 3 years ago |
Arijit Basu | 119adc6b57 | 3 years ago |
Arijit Basu | 250e21e949 | 3 years ago |
Arijit Basu | db8ef0e15a | 3 years ago |
Arijit Basu | ca211bb073 | 3 years ago |
Arijit Basu | 0cfeed7660 | 3 years ago |
Arijit Basu | ecc59b1d9d | 3 years ago |
Arijit Basu | 618ade53ec | 3 years ago |
Arijit Basu | 721ffd0216 | 3 years ago |
Arijit Basu | 26a363c7b1 | 3 years ago |
Arijit Basu | 74d5bb5e87 | 3 years ago |
Arijit Basu | 4f1e8e9021 | 3 years ago |
Arijit Basu | 7c3e74e46c | 3 years ago |
Arijit Basu | 5e20eea349 | 3 years ago |
Arijit Basu | 6b054cbc01 | 3 years ago |
Arijit Basu | c4ad648556 | 3 years ago |
Arijit Basu | 087f3b0d52 | 3 years ago |
Arijit Basu | bb65870ee0 | 3 years ago |
Arijit Basu | 7da7c54e86 | 3 years ago |
Arijit Basu | bb034180f4 | 3 years ago |
Arijit Basu | 1627e3974f | 3 years ago |
Arijit Basu | e6ea983a8a | 3 years ago |
Arijit Basu | 64c248f86d | 3 years ago |
Arijit Basu | ef0d37e9f6 | 3 years ago |
Arijit Basu | e2c3251736 | 3 years ago |
Arijit Basu | 4ff11c2ef4 | 3 years ago |
Arijit Basu | 24bd6d9f00 | 3 years ago |
Arijit Basu | 729b51d674 | 3 years ago |
Arijit Basu | 533a591d7f | 3 years ago |
Tom van Dijk | 17f3893198 | 3 years ago |
Arijit Basu | ebbce317a0 | 3 years ago |
Arijit Basu | 9cafe0d09f | 3 years ago |
Arijit Basu | 17036c98f0 | 3 years ago |
Arijit Basu | 7723f35944 | 3 years ago |
Arijit Basu | 95abe5e22b | 3 years ago |
Arijit Basu | cdd60eef4c | 3 years ago |
Arijit Basu | c89d5a45f1 | 3 years ago |
Arijit Basu | 59c8db821b | 3 years ago |
Arijit Basu | 7364812554 | 3 years ago |
Arijit Basu | 3142cdd3ec | 3 years ago |
Arijit Basu | 7b9e4deff5 | 3 years ago |
Arijit Basu | ea235b6969 | 3 years ago |
Arijit Basu | 8f5e5491f2 | 3 years ago |
Arijit Basu | e370c25bc5 | 3 years ago |
Arijit Basu | a97ff38e7a | 3 years ago |
Arijit Basu | ad2c7ab695 | 3 years ago |
Arijit Basu | 807f715f8f | 3 years ago |
Arijit Basu | 9683676587 | 3 years ago |
Arijit Basu | b7d86ae1ec | 3 years ago |
Tom van Dijk | 04dde7a477 | 3 years ago |
Tom van Dijk | b1ac4944ab | 3 years ago |
Tom van Dijk | 263eb5943a | 3 years ago |
Arijit Basu | def98de9b3 | 3 years ago |
Arijit Basu | 5b710070a3 | 3 years ago |
Arijit Basu | 41c387542f | 3 years ago |
Arijit Basu | 7c7351c2e8 | 3 years ago |
Arijit Basu | 5211e4fa40 | 3 years ago |
Arijit Basu | 05f048ce53 | 3 years ago |
Arijit Basu | b29c25c66f | 3 years ago |
Arijit Basu | 132b528a5d | 3 years ago |
Arijit Basu | d2c5f49835 | 3 years ago |
Arijit Basu | 0443c103e7 | 3 years ago |
Arijit Basu | 296d93a81a | 3 years ago |
Arijit Basu | 605b2bc391 | 3 years ago |
Arijit Basu | 9d088239ea | 3 years ago |
Igor Epstein | a5a71c9191 | 3 years ago |
Arijit Basu | 24637449a3 | 3 years ago |
Arijit Basu | f78d9e99fd | 3 years ago |
Arijit Basu | 2acfc97653 | 3 years ago |
Arijit Basu | 98799d0967 | 3 years ago |
Arijit Basu | 8e00b3e658 | 3 years ago |
Arijit Basu | 3692647a7a | 3 years ago |
Arijit Basu | 858b4066ca | 3 years ago |
Arijit Basu | 3ac39fd8f4 | 3 years ago |
Arijit Basu | b0c91a954e | 3 years ago |
Arijit Basu | 074217f21e | 3 years ago |
Arijit Basu | 48ab6eac21 | 3 years ago |
Arijit Basu | 6a3b26cc18 | 3 years ago |
Arijit Basu | 165d992b75 | 3 years ago |
Arijit Basu | 05c2f7aa68 | 3 years ago |
Arijit Basu | b45a553a0c | 3 years ago |
Arijit Basu | ca6cefb1c1 | 3 years ago |
Arijit Basu | 4495740cb0 | 3 years ago |
Shunsuke Mie | f5131b08ab | 3 years ago |
Arijit Basu | 00030e44ff | 3 years ago |
Arijit Basu | de6f54f9bd | 3 years ago |
Arijit Basu | a797d7b1c7 | 3 years ago |
Arijit Basu | 2d7158afc0 | 3 years ago |
Arijit Basu | 91675e28af | 3 years ago |
Tom van Dijk | 102832c65c | 3 years ago |
Tom van Dijk | 44f05c6e4b | 3 years ago |
Tom van Dijk | 3c8de699ca | 3 years ago |
Tom van Dijk | cbb244f9a0 | 3 years ago |
Arijit Basu | a81dd3f63f | 3 years ago |
Arijit Basu | abaaa91409 | 3 years ago |
jmechnich | 6efd29c3dd | 3 years ago |
Shunsuke Mie | 05fb583f98 | 3 years ago |
Shunsuke Mie | c5f60951f7 | 3 years ago |
Arijit Basu | ba3cf24125 | 3 years ago |
Shunsuke Mie | 1947d77a33 | 3 years ago |
Arijit Basu | 061d1b068a | 3 years ago |
Arijit Basu | 4e1580e91f | 3 years ago |
Shunsuke Mie | 41d1385020 | 3 years ago |
Arijit Basu | 66f5acc482 | 3 years ago |
Arijit Basu | 3ec870739c | 3 years ago |
Arijit Basu | 0328eb63c7 | 3 years ago |
Shunsuke Mie | 13d046e6ad | 3 years ago |
Shunsuke Mie | a3b400d749 | 3 years ago |
Arijit Basu | 947cfeb327 | 3 years ago |
Hans Tognon | 413430d5f8 | 3 years ago |
Arijit Basu | cd9880de71 | 3 years ago |
Arijit Basu | 385c8fd755 | 3 years ago |
Arijit Basu | 258937c307 | 3 years ago |
Arijit Basu | ee48fa1568 | 3 years ago |
Arijit Basu | 884cd98d0b | 3 years ago |
Zain ul abideen | 4ad4e0894d | 3 years ago |
Zain ul abideen | 8c9905f7f9 | 3 years ago |
Zain Ul abideen | 8bac4cf92a | 3 years ago |
Arijit Basu | 347b9625aa | 3 years ago |
Arijit Basu | 4bb54d49df | 3 years ago |
Gilad Woloch | 402715cdcd | 3 years ago |
Gilad Woloch | 95b580203f | 3 years ago |
Gilad Woloch | 18b784f054 | 3 years ago |
Gilad Woloch | 2ef7523bb0 | 3 years ago |
Gilad Woloch | 46287f6373 | 3 years ago |
Gilad Woloch | 45ad449eb3 | 3 years ago |
Arijit Basu | 7142c97c7f | 3 years ago |
Gilad Woloch | a2343c05ca | 3 years ago |
Gilad Woloch | cc022e85ff | 3 years ago |
Gilad Woloch | c399236fd3 | 3 years ago |
Gilad Woloch | 3f0e479f56 | 3 years ago |
Arijit Basu | 9982a205db | 3 years ago |
Arijit Basu | bec80e98df | 3 years ago |
Gilad Woloch | ac1b40799a | 3 years ago |
Gilad Woloch | 394c09ae87 | 3 years ago |
Gilad Woloch | a1ee7f931c | 3 years ago |
Arijit Basu | b2ad4a1303 | 3 years ago |
@ -1,4 +1,14 @@
|
||||
# Why dynamic linking?
|
||||
# See https://github.com/sayanarijit/xplr/issues/309
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.aarch64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "linker=aarch64-linux-gnu-gcc", "-C", "link-args=-rdynamic"]
|
||||
|
||||
[target.aarch64-linux-android]
|
||||
rustflags = ["-C", "linker=aarch64-linux-android-clang", "-C", "link-args=-rdynamic", "-C", "default-linker-libraries"]
|
||||
|
||||
[target.arm-unknown-linux-gnueabihf]
|
||||
rustflags = ["-C", "linker=arm-linux-gnueabihf-gcc", "-C", "link-args=-rdynamic"]
|
||||
|
@ -0,0 +1,6 @@
|
||||
ratatui
|
||||
crate
|
||||
ser
|
||||
enque
|
||||
noice
|
||||
ans
|
@ -1,107 +1,144 @@
|
||||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: main
|
||||
workflow_dispatch:
|
||||
|
||||
name: Continuous Integration
|
||||
|
||||
jobs:
|
||||
# Workaround for making Github Actions skip based on commit message `[skip ci]`
|
||||
# Source https://gist.github.com/ybiquitous/c80f15c18319c63cae8447a3be341267
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
!contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title, github.event.pull_request.body), '[skip ci]')
|
||||
steps:
|
||||
- run: |
|
||||
cat <<'MESSAGE'
|
||||
github.event_name: ${{ toJson(github.event_name) }}
|
||||
github.event:
|
||||
${{ toJson(github.event) }}
|
||||
MESSAGE
|
||||
check:
|
||||
name: Check
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
|
||||
test:
|
||||
name: Test Suite
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
# These dependencies are required for `clipboard`
|
||||
- run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
|
||||
# bench:
|
||||
# name: Benchmarks
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: prepare
|
||||
# steps:
|
||||
# - uses: actions/checkout@master
|
||||
# - uses: actions-rs/toolchain@v1
|
||||
# with:
|
||||
# toolchain: stable
|
||||
# profile: minimal
|
||||
# override: true
|
||||
# # These dependencies are required for `clipboard`
|
||||
# - run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: bench
|
||||
- run: cargo check
|
||||
|
||||
fmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
components: rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
- run: cargo fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
needs: prepare
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
components: clippy
|
||||
- uses: actions-rs/cargo@v1
|
||||
- run: cargo clippy -- -D warnings
|
||||
|
||||
spellcheck:
|
||||
name: Spellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: codespell-project/actions-codespell@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
ignore_words_file: .codespellignore
|
||||
|
||||
test:
|
||||
name: Test Suite
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs:
|
||||
- check
|
||||
- fmt
|
||||
- clippy
|
||||
- spellcheck
|
||||
strategy:
|
||||
matrix:
|
||||
build:
|
||||
- macos
|
||||
- macos-aarch64
|
||||
- linux
|
||||
- linux-musl
|
||||
- linux-aarch64
|
||||
- linux-arm
|
||||
rust: [stable]
|
||||
include:
|
||||
# See the list: https://github.com/cross-rs/cross
|
||||
|
||||
- build: macos
|
||||
os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
|
||||
- build: macos-aarch64
|
||||
os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
|
||||
- build: linux
|
||||
os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
||||
- build: linux-musl
|
||||
os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
|
||||
- build: linux-aarch64
|
||||
os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-gnu
|
||||
|
||||
- build: linux-arm
|
||||
os: ubuntu-latest
|
||||
target: arm-unknown-linux-gnueabihf
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: full
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Installing Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Installing needed macOS dependencies
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: brew install openssl@1.1
|
||||
|
||||
- name: Installing needed Ubuntu dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update --fix-missing
|
||||
sudo apt-get install -y --no-install-recommends liblua5.1-0-dev libluajit-5.1-dev gcc pkg-config curl git make ca-certificates
|
||||
|
||||
- if: matrix.build == 'linux-musl'
|
||||
run: sudo apt-get install -y musl-tools
|
||||
|
||||
- if: matrix.build == 'linux-aarch64'
|
||||
run: sudo apt-get install -y gcc-aarch64-linux-gnu
|
||||
|
||||
- if: matrix.build == 'linux-arm'
|
||||
run: |
|
||||
sudo apt-get install -y gcc-multilib
|
||||
sudo apt-get install -y gcc-arm-linux-gnueabihf
|
||||
sudo ln -s /usr/include/asm-generic/ /usr/include/asm
|
||||
|
||||
- run: cargo build --target ${{ matrix.target }}
|
||||
|
||||
- if: matrix.build == 'macos' || matrix.build == 'linux'
|
||||
run: cargo test --target ${{ matrix.target }}
|
||||
|
||||
# bench:
|
||||
# name: Benchmarks
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: dtolnay/rust-toolchain@stable
|
||||
# with:
|
||||
# toolchain: stable
|
||||
# # These dependencies are required for `clipboard`
|
||||
# - run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
|
||||
# - run: cargo bench
|
||||
|
@ -0,0 +1,23 @@
|
||||
name: "Push Binary Cache for Nix"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: nixbuild/nix-quick-install-action@v19
|
||||
with:
|
||||
nix_conf: experimental-features = nix-command flakes
|
||||
- uses: cachix/cachix-action@v11
|
||||
with:
|
||||
name: xplr
|
||||
authtoken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
- run: nix profile install .
|
||||
- name: Run tests
|
||||
run: |
|
||||
xplr --version
|
File diff suppressed because it is too large
Load Diff
@ -1,50 +1,94 @@
|
||||
[package]
|
||||
name = "xplr"
|
||||
version = "0.15.0" # Update lua.rs
|
||||
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "A hackable, minimal, fast TUI file explorer"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/sayanarijit/xplr"
|
||||
homepage = "https://xplr.dev"
|
||||
documentation = "https://xplr.dev/en"
|
||||
keywords = ["terminal", "file", "explorer", "manager", "tui"]
|
||||
categories = ["command-line-interface", "command-line-utilities"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "xplr"
|
||||
name = 'xplr'
|
||||
|
||||
[[bench]]
|
||||
name = 'criterion'
|
||||
harness = false
|
||||
path = './benches/criterion.rs'
|
||||
|
||||
[package]
|
||||
name = 'xplr'
|
||||
version = '0.21.9'
|
||||
authors = ['Arijit Basu <hi@arijitbasu.in>']
|
||||
edition = '2021'
|
||||
description = 'A hackable, minimal, fast TUI file explorer'
|
||||
license = 'MIT'
|
||||
readme = 'README.md'
|
||||
repository = 'https://github.com/sayanarijit/xplr'
|
||||
homepage = 'https://xplr.dev'
|
||||
documentation = 'https://xplr.dev/en'
|
||||
keywords = ['terminal', 'file', 'explorer', 'manager', 'tui']
|
||||
categories = ['command-line-interface', 'command-line-utilities']
|
||||
include = ['src/**/*', 'docs/en/src/**/*', 'LICENSE', 'README.md']
|
||||
|
||||
[dependencies]
|
||||
tui = { version = "0.16.0", default-features = false, features = ['crossterm', 'serde'] }
|
||||
crossterm = "0.21.0"
|
||||
dirs = "4.0.0"
|
||||
serde = { version = "1.0.130", features = ["derive"] }
|
||||
serde_yaml = "0.8.21"
|
||||
mime_guess = "2.0.3"
|
||||
anyhow = "1.0.44"
|
||||
chrono = { version = "0.4.19", features = ["serde"] }
|
||||
lazy_static = "1.4.0"
|
||||
indexmap = { version = "1.7.0", features = ["serde"] }
|
||||
libc = "0.2.155"
|
||||
humansize = "2.1.3"
|
||||
natord = "1.0.9"
|
||||
humansize = "1.1.1"
|
||||
mlua = { version = "0.6.4", features = ["luajit", "vendored", "serialize", "send"] }
|
||||
ansi-to-tui = "0.4.1"
|
||||
libc = "0.2.103"
|
||||
anyhow = "1.0.86"
|
||||
serde_yaml = "0.9.34"
|
||||
crossterm = { version = "0.27.0", features = [], default-features = false }
|
||||
ansi-to-tui = "=3.1.0"
|
||||
regex = "1.10.5"
|
||||
gethostname = "0.4.3"
|
||||
serde_json = "1.0.117"
|
||||
path-absolutize = "3.1.1"
|
||||
which = "6.0.1"
|
||||
nu-ansi-term = "0.50.0"
|
||||
textwrap = "0.16"
|
||||
snailquote = "0.3.1"
|
||||
skim = { version = "0.10.4", default-features = false }
|
||||
time = { version = "0.3.36", features = ["serde", "local-offset", "formatting", "macros"] }
|
||||
jf = "0.6.2"
|
||||
xdg = "2.5.2"
|
||||
home = "0.5.9"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3.5"
|
||||
assert_cmd = "2.0.1"
|
||||
[dependencies.lscolors]
|
||||
version = "0.17.0"
|
||||
default-features = false
|
||||
features = ["nu-ansi-term"]
|
||||
|
||||
[[bench]]
|
||||
name = "criterion"
|
||||
harness = false
|
||||
[dependencies.lazy_static]
|
||||
version = "1.4.0"
|
||||
default-features = false
|
||||
|
||||
[dependencies.mime_guess]
|
||||
version = "2.0.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.tui]
|
||||
version = "=0.26.1" # https://github.com/ratatui-org/ratatui/issues/1032
|
||||
default-features = false
|
||||
features = ['crossterm', 'serde']
|
||||
package = 'ratatui'
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.203"
|
||||
features = []
|
||||
default-features = false
|
||||
|
||||
[dependencies.indexmap]
|
||||
version = "2.2.6"
|
||||
features = ['serde']
|
||||
|
||||
[dependencies.mlua]
|
||||
version = "0.9.8"
|
||||
features = ['luajit', 'serialize', 'send']
|
||||
|
||||
[dependencies.tui-input]
|
||||
version = "0.8.0"
|
||||
features = ['serde']
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5.1"
|
||||
assert_cmd = "2.0.14"
|
||||
|
||||
# https://github.com/johnthagen/min-sized-rust
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = 'abort'
|
||||
strip = true
|
||||
|
||||
[features]
|
||||
default = ["vendored-lua"]
|
||||
vendored-lua = ["mlua/vendored"]
|
||||
|
@ -1,7 +1,17 @@
|
||||
Process
|
||||
-------
|
||||
- [ ] Update `Cargo.toml` version.
|
||||
- [ ] Update `config.yml` version.
|
||||
- [ ] Update `Config::is_compatible()` and `Config::upgrade_notification()`.
|
||||
- [ ] Publish GitHub tag.
|
||||
- [ ] Update Wiki and docs.
|
||||
# Build
|
||||
|
||||
See [install.md](./docs/en/src/install.md#build-from-source)
|
||||
|
||||
Note: xplr ships with vendored luajit. If the platform can't compile this,
|
||||
you need to compile using `--no-default-features` argument to avoid using
|
||||
vendored luajit, so that you can static link luajit yourself.
|
||||
|
||||
# Release
|
||||
|
||||
The final binary `target/release/xplr` can be shipped with the following assets
|
||||
|
||||
- [License](./LICENSE)
|
||||
- [Desktop Entry](./assets/desktop/xplr.desktop)
|
||||
- [Desktop Icons](./assets/icon/)
|
||||
- [Offline Docs](./docs/en/src)
|
||||
- [Lua Configuration Example](./src/init.lua)
|
||||
|
@ -0,0 +1,10 @@
|
||||
(import
|
||||
(
|
||||
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
||||
fetchTarball {
|
||||
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||
}
|
||||
)
|
||||
{ src = ./.; }
|
||||
).defaultNix
|
@ -1,11 +1,16 @@
|
||||
[book]
|
||||
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
|
||||
title = "xplr book"
|
||||
description = "A hackable, minimal, fast TUI file explorer"
|
||||
src = "src"
|
||||
language = "en"
|
||||
authors = ['Arijit Basu <hi@arijitbasu.in>']
|
||||
title = 'xplr book'
|
||||
description = 'A hackable, minimal, fast TUI file explorer'
|
||||
src = 'src'
|
||||
language = 'en'
|
||||
|
||||
[output.html]
|
||||
site-url = "/xplr/en/"
|
||||
git-repository-url = "https://github.com/sayanarijit/xplr"
|
||||
edit-url-template = "https://github.com/sayanarijit/xplr/edit/main/docs/en/{path}"
|
||||
site-url = '/xplr/en/'
|
||||
git-repository-url = 'https://github.com/sayanarijit/xplr'
|
||||
edit-url-template = 'https://github.com/sayanarijit/xplr/edit/main/docs/en/{path}'
|
||||
default-theme = 'dark'
|
||||
preferred-dark-theme = 'coal'
|
||||
|
||||
[output.linkcheck]
|
||||
optional = true
|
||||
|
@ -1,57 +1,81 @@
|
||||
A hackable, minimal, fast TUI file explorer
|
||||
===========================================
|
||||
# A hackable, minimal, fast TUI file explorer
|
||||
|
||||
- [Introduction][1]
|
||||
- [Quickstart][2]
|
||||
- [Install][3]
|
||||
- [Post Install][4]
|
||||
- [Configuration][5]
|
||||
- [General Config][6]
|
||||
- [General][6]
|
||||
- [Node Types][10]
|
||||
- [Layouts][9]
|
||||
- [Modes][7]
|
||||
- [Concept][32]
|
||||
- [Sum Type][42]
|
||||
- [Key Bindings][27]
|
||||
- [Configure Key Bindings][28]
|
||||
- [Default Key Bindings][14]
|
||||
- [Debug Key Bindings][29]
|
||||
- [Node Type][33]
|
||||
- [Layout][34]
|
||||
- [Mode][35]
|
||||
- [Message][8]
|
||||
- [Layouts][9]
|
||||
- [Node Types][10]
|
||||
- [Full List of Messages][38]
|
||||
- [Input Operation][39]
|
||||
- [Borders][31]
|
||||
- [Style][11]
|
||||
- [Searching][41]
|
||||
- [Sorting][12]
|
||||
- [Filtering][13]
|
||||
- [Column Renderer][26]
|
||||
- [Default Key Bindings][14]
|
||||
- [Lua Function Calls][36]
|
||||
- [xplr.util][40]
|
||||
- [Environment Variables and Pipes][37]
|
||||
- [Awesome Hacks][30]
|
||||
- [Plugin][15]
|
||||
- [Installing Plugins][16]
|
||||
- [Writing Plugins][17]
|
||||
- [Awesome Plugins][18]
|
||||
- [Integration][19]
|
||||
- [Awesome Integrations][20]
|
||||
- [TODO][21]
|
||||
- [Alternatives][22]
|
||||
- [Upgrade Guide][23]
|
||||
- [Community][24]
|
||||
- [Contribute][25]
|
||||
|
||||
|
||||
[1]:introduction.md
|
||||
[2]:quickstart.md
|
||||
[3]:install.md
|
||||
[4]:post-install.md
|
||||
[5]:configuration.md
|
||||
[6]:general-config.md
|
||||
[7]:modes.md
|
||||
[8]:message.md
|
||||
[9]:layouts.md
|
||||
[10]:node_types.md
|
||||
[11]:style.md
|
||||
[12]:sorting.md
|
||||
[13]:filtering.md
|
||||
[14]:default-key-bindings.md
|
||||
[15]:plugin.md
|
||||
[16]:installing-plugins.md
|
||||
[17]:writing-plugins.md
|
||||
[18]:awesome-plugins.md
|
||||
[19]:integration.md
|
||||
[20]:awesome-integrations.md
|
||||
[21]:todo.md
|
||||
[22]:alternatives.md
|
||||
[23]:upgrade-guide.md
|
||||
[24]:community.md
|
||||
[25]:contribute.md
|
||||
[26]:column-renderer.md
|
||||
[1]: introduction.md
|
||||
[2]: quickstart.md
|
||||
[3]: install.md
|
||||
[4]: post-install.md
|
||||
[5]: configuration.md
|
||||
[6]: general-config.md
|
||||
[7]: modes.md
|
||||
[8]: message.md
|
||||
[9]: layouts.md
|
||||
[10]: node_types.md
|
||||
[11]: style.md
|
||||
[12]: sorting.md
|
||||
[13]: filtering.md
|
||||
[14]: default-key-bindings.md
|
||||
[15]: plugin.md
|
||||
[16]: installing-plugins.md
|
||||
[17]: writing-plugins.md
|
||||
[18]: awesome-plugins.md
|
||||
[19]: integration.md
|
||||
[20]: awesome-integrations.md
|
||||
[22]: alternatives.md
|
||||
[23]: upgrade-guide.md
|
||||
[26]: column-renderer.md
|
||||
[27]: key-bindings.md
|
||||
[28]: configure-key-bindings.md
|
||||
[29]: debug-key-bindings.md
|
||||
[30]: awesome-hacks.md
|
||||
[31]: borders.md
|
||||
[32]: concept.md
|
||||
[33]: node-type.md
|
||||
[34]: layout.md
|
||||
[35]: mode.md
|
||||
[36]: lua-function-calls.md
|
||||
[37]: environment-variables-and-pipes.md
|
||||
[38]: messages.md
|
||||
[39]: input-operation.md
|
||||
[40]: xplr.util.md
|
||||
[41]: searching.md
|
||||
[42]: sum-type.md
|
||||
|
@ -0,0 +1,571 @@
|
||||
# Awesome Hacks
|
||||
|
||||
Here's a list of cool xplr hacks, i.e. snippets of code that you can just copy
|
||||
and paste into your [configuration][1] or the appropriate file, that are
|
||||
too small or too niche for a full fledge [plugin][2].
|
||||
|
||||
Do you have something cool to share?
|
||||
|
||||
[Edit this file][3] or [share them here][4].
|
||||
|
||||
You can try these hacks by writing them to a file, say `hack.lua` and passing
|
||||
it to xplr with `--extra-config` or `-C`.
|
||||
|
||||
```bash
|
||||
xplr -C hack.lua
|
||||
```
|
||||
|
||||
### cd on quit
|
||||
|
||||
Change directory using xplr.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Tested on: Linux
|
||||
|
||||
NOTE: This is a shell hack, rather than Lua config hack. Add this in
|
||||
`.bashrc` or `.profile` file in your home directory.
|
||||
|
||||
With this alias set, you can navigate directories using xplr by entering
|
||||
xcd command, and when you quit by pressing enter, you will enter the
|
||||
directory.
|
||||
|
||||
You can of course, quit with plain Quit (i.e. by pressing q) to
|
||||
gracefully cancel "cd on quit".
|
||||
|
||||
```bash
|
||||
alias xcd='cd "$(xplr --print-pwd-as-result)"'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Spawn multiple sessions in different tabs (iTerm2)
|
||||
|
||||
Creating a new session that starts with iTerm2.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@lmburns][9]
|
||||
- Requires: iTerm2
|
||||
- Tested on: MacOS
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key["ctrl-n"] = {
|
||||
help = "new session",
|
||||
messages = {
|
||||
{ BashExecSilently = [[
|
||||
osascript <<EOF
|
||||
tell application "iTerm2"
|
||||
tell current window
|
||||
create tab with default profile
|
||||
tell current session to write text "xplr"
|
||||
end tell
|
||||
end tell
|
||||
]]
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Bookmark
|
||||
|
||||
Bookmark files using `m` and fuzzy search bookmarks using backtick.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
[![xplr-bookmark.gif][7]][6]
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Requires: fzf
|
||||
- Tested on: Linux
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key.m = {
|
||||
help = "bookmark",
|
||||
messages = {
|
||||
{
|
||||
BashExecSilently0 = [===[
|
||||
PTH="${XPLR_FOCUS_PATH:?}"
|
||||
PTH_ESC=$(printf %q "$PTH")
|
||||
if echo "${PTH:?}" >> "${XPLR_SESSION_PATH:?}/bookmarks"; then
|
||||
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC added to bookmarks"
|
||||
else
|
||||
"$XPLR" -m 'LogError: %q' "Failed to bookmark $PTH_ESC"
|
||||
fi
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key["`"] = {
|
||||
help = "go to bookmark",
|
||||
messages = {
|
||||
{
|
||||
BashExec0 = [===[
|
||||
PTH=$(cat "${XPLR_SESSION_PATH:?}/bookmarks" | fzf --no-sort)
|
||||
PTH_ESC=$(printf %q "$PTH")
|
||||
if [ "$PTH" ]; then
|
||||
"$XPLR" -m 'FocusPath: %q' "$PTH"
|
||||
fi
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Persistent, multi-session bookmark
|
||||
|
||||
A bookmark mode that allows for a bookmark file to be used throughout multiples
|
||||
sessions. It is set to the environment variable `$XPLR_BOOKMARK_FILE`. A
|
||||
bookmark can be added, deleted, or jumped to.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@lmburns][9]
|
||||
- Requires: fzf, sd
|
||||
- Tested on: MacOS
|
||||
|
||||
```lua
|
||||
-- With `export XPLR_BOOKMARK_FILE="$HOME/bookmarks"`
|
||||
-- Bookmark: mode binding
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key["b"] = {
|
||||
help = "bookmark mode",
|
||||
messages = {
|
||||
{ SwitchModeCustom = "bookmark" },
|
||||
},
|
||||
}
|
||||
xplr.config.modes.custom.bookmark = {
|
||||
name = "bookmark",
|
||||
key_bindings = {
|
||||
on_key = {
|
||||
m = {
|
||||
help = "bookmark dir",
|
||||
messages = {
|
||||
{
|
||||
BashExecSilently0 = [[
|
||||
PTH="${XPLR_FOCUS_PATH:?}"
|
||||
if [ -d "${PTH}" ]; then
|
||||
PTH="${PTH}"
|
||||
elif [ -f "${PTH}" ]; then
|
||||
PTH=$(dirname "${PTH}")
|
||||
fi
|
||||
PTH_ESC=$(printf %q "$PTH")
|
||||
if echo "${PTH:?}" >> "${XPLR_BOOKMARK_FILE:?}"; then
|
||||
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC added to bookmarks"
|
||||
else
|
||||
"$XPLR" -m 'LogError: %q' "Failed to bookmark $PTH_ESC"
|
||||
fi
|
||||
]],
|
||||
},
|
||||
"PopMode",
|
||||
},
|
||||
},
|
||||
g = {
|
||||
help = "go to bookmark",
|
||||
messages = {
|
||||
{
|
||||
BashExec0 = [===[
|
||||
PTH=$(cat "${XPLR_BOOKMARK_FILE:?}" | fzf --no-sort)
|
||||
if [ "$PTH" ]; then
|
||||
"$XPLR" -m 'FocusPath: %q' "$PTH"
|
||||
fi
|
||||
]===],
|
||||
},
|
||||
"PopMode",
|
||||
},
|
||||
},
|
||||
d = {
|
||||
help = "delete bookmark",
|
||||
messages = {
|
||||
{
|
||||
BashExec0 = [[
|
||||
PTH=$(cat "${XPLR_BOOKMARK_FILE:?}" | fzf --no-sort)
|
||||
sd "$PTH\n" "" "${XPLR_BOOKMARK_FILE:?}"
|
||||
]],
|
||||
},
|
||||
"PopMode",
|
||||
},
|
||||
},
|
||||
esc = {
|
||||
help = "cancel",
|
||||
messages = {
|
||||
"PopMode",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Another bookmark manager type thing, taken from [wfxr's zsh plugin][13].
|
||||
|
||||
Another bookmark manager type thing, taken from [wfxr's zsh plugin][13] which has colored output with fzf.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@lmburns][9]
|
||||
- Requires: fzf, exa
|
||||
- Tested on: MacOS
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.go_to.key_bindings.on_key.b = {
|
||||
help = "bookmark jump",
|
||||
messages = {
|
||||
"PopMode",
|
||||
{ BashExec0 = [===[
|
||||
field='\(\S\+\s*\)'
|
||||
esc=$(printf '\033')
|
||||
N="${esc}[0m"
|
||||
R="${esc}[31m"
|
||||
G="${esc}[32m"
|
||||
Y="${esc}[33m"
|
||||
B="${esc}[34m"
|
||||
pattern="s#^${field}${field}${field}${field}#$Y\1$R\2$N\3$B\4$N#"
|
||||
PTH=$(sed 's#: # -> #' "$PATHMARKS_FILE"| nl| column -t \
|
||||
| gsed "${pattern}" \
|
||||
| fzf --ansi \
|
||||
--height '40%' \
|
||||
--preview="echo {}|sed 's#.*-> ##'| xargs exa --color=always" \
|
||||
--preview-window="right:50%" \
|
||||
| sed 's#.*-> ##')
|
||||
if [ "$PTH" ]; then
|
||||
"$XPLR" -m 'ChangeDirectory: %q' "$PTH"
|
||||
fi
|
||||
]===]
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Fuzzy search history
|
||||
|
||||
Fuzzy search the last visited directories.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Requires: fzf
|
||||
- Tested on: Linux
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.go_to.key_bindings.on_key.h = {
|
||||
help = "history",
|
||||
messages = {
|
||||
"PopMode",
|
||||
{
|
||||
BashExec0 = [===[
|
||||
PTH=$(cat "${XPLR_PIPE_HISTORY_OUT:?}" | sort -z -u | fzf --read0)
|
||||
if [ "$PTH" ]; then
|
||||
"$XPLR" -m 'ChangeDirectory: %q' "$PTH"
|
||||
fi
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Batch rename
|
||||
|
||||
Batch rename the selected or visible files and directories in $PWD.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
[![xplr-rename.gif][11]][10]
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Requires: [pipe-rename][12]
|
||||
- Tested on: Linux
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key.R = {
|
||||
help = "batch rename",
|
||||
messages = {
|
||||
{
|
||||
BashExec = [===[
|
||||
SELECTION=$(cat "${XPLR_PIPE_SELECTION_OUT:?}")
|
||||
NODES=${SELECTION:-$(cat "${XPLR_PIPE_DIRECTORY_NODES_OUT:?}")}
|
||||
if [ "$NODES" ]; then
|
||||
echo -e "$NODES" | renamer
|
||||
"$XPLR" -m ExplorePwdAsync
|
||||
fi
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Serve $PWD
|
||||
|
||||
Serve $PWD using a static web server via LAN.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Requires: [sfz][14], fzf
|
||||
- Tested on: Linux
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key.S = {
|
||||
help = "serve $PWD",
|
||||
messages = {
|
||||
{
|
||||
BashExec0 = [===[
|
||||
IP=$(ip addr | grep -w inet | cut -d/ -f1 | grep -Eo '[0-9]{1,3}\.[0-9]{ 1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | fzf --prompt 'Select IP > ')
|
||||
echo "IP: ${IP:?}"
|
||||
read -p "Port (default 5000): " PORT
|
||||
echo
|
||||
sfz --all --cors --no-ignore --bind ${IP:?} --port ${PORT:-5000} . &
|
||||
sleep 1 && read -p '[press enter to exit]'
|
||||
kill -9 %1
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Image viewer (imv)
|
||||
|
||||
Preview images using [imv][17].
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Requires: [imv][17], [xdotool][18]
|
||||
- Tested on: Linux, FreeBSD 13.1-RELEASE
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key.P = {
|
||||
help = "preview",
|
||||
messages = {
|
||||
{
|
||||
BashExecSilently0 = [===[
|
||||
FIFO_PATH="/tmp/xplr.fifo"
|
||||
|
||||
if [ -e "$FIFO_PATH" ]; then
|
||||
"$XPLR" -m StopFifo
|
||||
rm -f -- "$FIFO_PATH"
|
||||
else
|
||||
mkfifo "$FIFO_PATH"
|
||||
"$HOME/.local/bin/imv-open.sh" "$FIFO_PATH" "$XPLR_FOCUS_PATH" &
|
||||
"$XPLR" -m 'StartFifo: %q' "$FIFO_PATH"
|
||||
fi
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
$HOME/.local/bin/imv-open.sh
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
FIFO_PATH="$1"
|
||||
IMAGE="$2"
|
||||
MAINWINDOW="$(xdotool getactivewindow)"
|
||||
IMV_PID="$(pgrep imv)"
|
||||
|
||||
if [ ! "$IMV_PID" ]; then
|
||||
imv "$IMAGE" &
|
||||
IMV_PID=$!
|
||||
fi
|
||||
|
||||
sleep 0.5
|
||||
|
||||
xdotool windowactivate "$MAINWINDOW"
|
||||
|
||||
while read -r path; do
|
||||
imv-msg "$IMV_PID" close all
|
||||
imv-msg "$IMV_PID" open "$path"
|
||||
done < "$FIFO_PATH"
|
||||
|
||||
imv-msg "$IMV_PID" quit
|
||||
[ -e "$FIFO_PATH" ] && rm -f -- "$FIFO_PATH"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Text preview pane
|
||||
|
||||
Preview text files in a native xplr pane (should be fast enough).
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Requires: none
|
||||
- Tested on: Linux, FreeBSD 13.1-RELEASE
|
||||
|
||||
```lua
|
||||
local function stat(node)
|
||||
return xplr.util.to_yaml(xplr.util.node(node.absolute_path))
|
||||
end
|
||||
|
||||
local function read(path, height)
|
||||
local p = io.open(path)
|
||||
|
||||
if p == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local i = 0
|
||||
local res = ""
|
||||
for line in p:lines() do
|
||||
if line:match("[^ -~\n\t]") then
|
||||
p:close()
|
||||
return
|
||||
end
|
||||
|
||||
res = res .. line .. "\n"
|
||||
if i == height then
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
p:close()
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
xplr.fn.custom.preview_pane = {}
|
||||
xplr.fn.custom.preview_pane.render = function(ctx)
|
||||
local title = nil
|
||||
local body = ""
|
||||
local n = ctx.app.focused_node
|
||||
if n and n.canonical then
|
||||
n = n.canonical
|
||||
end
|
||||
|
||||
if n then
|
||||
title = { format = n.absolute_path, style = xplr.util.lscolor(n.absolute_path) }
|
||||
if n.is_file then
|
||||
body = read(n.absolute_path, ctx.layout_size.height) or stat(n)
|
||||
else
|
||||
body = stat(n)
|
||||
end
|
||||
end
|
||||
|
||||
return { CustomParagraph = { ui = { title = title }, body = body } }
|
||||
end
|
||||
|
||||
local preview_pane = { Dynamic = "custom.preview_pane.render" }
|
||||
local split_preview = {
|
||||
Horizontal = {
|
||||
config = {
|
||||
constraints = {
|
||||
{ Percentage = 60 },
|
||||
{ Percentage = 40 },
|
||||
},
|
||||
},
|
||||
splits = {
|
||||
"Table",
|
||||
preview_pane,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
xplr.config.layouts.builtin.default =
|
||||
xplr.util.layout_replace(xplr.config.layouts.builtin.default, "Table", split_preview)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Tere Navigation
|
||||
|
||||
Navigate using the [tere][19] file explorer (defaults to type-to-nav system).
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@sayanarijit][8]
|
||||
- Requires: [tere][19]
|
||||
- Tested on: Linux
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key.T = {
|
||||
help = "tere nav",
|
||||
messages = {
|
||||
{ BashExec0 = [["$XPLR" -m 'ChangeDirectory: %q' "$(tere)"]] },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Toggle exe
|
||||
|
||||
Toggle the execute permission on the focused node.
|
||||
|
||||
<details>
|
||||
<summary>Expand for details</summary>
|
||||
|
||||
- Author: [@doums](https://github.com/doums)
|
||||
- Requires: chmod
|
||||
- Tested on: Linux
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key['*'] = {
|
||||
help = 'toggle exe',
|
||||
messages = {
|
||||
{
|
||||
BashExecSilently0 = [===[
|
||||
f="$XPLR_FOCUS_PATH"
|
||||
if [ -x "$f" ]; then chmod -x "$f"; else chmod +x "$f"; fi
|
||||
"$XPLR" -m 'ExplorePwd'
|
||||
"$XPLR" -m 'FocusPath: %q' "$f"
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Also See:
|
||||
|
||||
- [Awesome Plugins][15]
|
||||
- [Awesome Integrations][16]
|
||||
|
||||
[1]: configuration.md
|
||||
[2]: plugin.md
|
||||
[3]: https://github.com/sayanarijit/xplr/edit/main/docs/en/src/awesome-hacks.md
|
||||
[4]: https://github.com/sayanarijit/xplr/discussions/categories/show-and-tell
|
||||
[6]: https://gifyu.com/image/rGSR
|
||||
[7]: https://s4.gifyu.com/images/xplr-bookmark.gif
|
||||
[8]: https://github.com/sayanarijit
|
||||
[9]: https://github.com/lmburns
|
||||
[10]: https://gifyu.com/image/rGbo
|
||||
[11]: https://s4.gifyu.com/images/xplr-rename.gif
|
||||
[12]: https://github.com/marcusbuffett/pipe-rename
|
||||
[13]: https://github.com/wfxr/formarks
|
||||
[14]: https://github.com/weihanglo/sfz
|
||||
[15]: awesome-plugins.md
|
||||
[16]: awesome-integrations.md
|
||||
[17]: https://sr.ht/~exec64/imv
|
||||
[18]: https://www.semicomplete.com/projects/xdotool
|
||||
[19]: https://github.com/mgunyho/tere
|
@ -1,46 +1,42 @@
|
||||
Awesome Integrations
|
||||
====================
|
||||
# Awesome Integrations
|
||||
|
||||
Here's a list of awesome xplr integrations that you might want to check out.
|
||||
|
||||
If none of the following integrations work for you, you can create your own and
|
||||
[let us know][1].
|
||||
|
||||
### Editor
|
||||
|
||||
Categories
|
||||
----------
|
||||
|
||||
- [Editor][2]
|
||||
- [Shell][3]
|
||||
- [Security Tools][4]
|
||||
|
||||
|
||||
Editor
|
||||
------
|
||||
|
||||
- [**fm-nvim**][10] Neovim plugin that lets you use your favorite terminal file managers from within Neovim.
|
||||
- [**vim-floaterm**][6] xplr integrated in vim-floaterm (Neo)vim plugin.
|
||||
- [**xplr.nvim**][9] Opens xplr inside nvim, and hosts a msgpack client inside xplr.
|
||||
- [**xplr.vim**][5] Pick files in Vim using xplr.
|
||||
|
||||
### Github
|
||||
|
||||
Shell
|
||||
-----
|
||||
- [**gh-xplr**][13] Explore GitHub repos using xplr via GitHub CLI.
|
||||
|
||||
- [**powerlevel10k**][7] Powerlevel10k prompt for xplr shell.
|
||||
### Shell
|
||||
|
||||
- [**powerlevel10k**][7] Powerlevel10k prompt for xplr shell.
|
||||
|
||||
Security Tools
|
||||
--------------
|
||||
### Security Tools
|
||||
|
||||
- [**gpg-tui**][8] Import GPG certificates using xplr.
|
||||
|
||||
|
||||
[1]:https://github.com/sayanarijit/xplr/discussions/categories/show-and-tell
|
||||
[2]:#editor
|
||||
[3]:#shell
|
||||
[4]:#security-tools
|
||||
[5]:https://github.com/sayanarijit/xplr.vim
|
||||
[6]:https://github.com/voldikss/vim-floaterm#xplr
|
||||
[7]:https://github.com/romkatv/powerlevel10k/blob/191d1b89e325ee3b6d2d75a394654aaf4f077a7c/internal/p10k.zsh#L4756-L4768
|
||||
[8]:https://github.com/orhun/gpg-tui#importreceive
|
||||
[9]:https://github.com/fhill2/xplr.nvim
|
||||
## Also See:
|
||||
|
||||
- [Awesome Hacks][11]
|
||||
- [Awesome Plugins][12]
|
||||
|
||||
[1]: https://github.com/sayanarijit/xplr/discussions/categories/show-and-tell
|
||||
[2]: #editor
|
||||
[3]: #shell
|
||||
[4]: #security-tools
|
||||
[5]: https://github.com/sayanarijit/xplr.vim
|
||||
[6]: https://github.com/voldikss/vim-floaterm#xplr
|
||||
[7]: https://github.com/romkatv/powerlevel10k/blob/191d1b89e325ee3b6d2d75a394654aaf4f077a7c/internal/p10k.zsh#L4756-L4768
|
||||
[8]: https://github.com/orhun/gpg-tui#importreceive
|
||||
[10]: https://github.com/is0n/fm-nvim
|
||||
[11]: awesome-hacks.md
|
||||
[12]: awesome-plugins.md
|
||||
[13]: https://github.com/sayanarijit/gh-xplr
|
||||
|
@ -1,94 +1,128 @@
|
||||
Awesome Plugins
|
||||
===============
|
||||
# Awesome Plugins
|
||||
|
||||
Here's a list of awesome xplr plugins that you might want to check out. If none
|
||||
Here's a list of awesome xplr plugins that you might want to [check out][48]. If none
|
||||
of the following plugins work for you, it's very easy to
|
||||
[write your own][1].
|
||||
|
||||
### Extension
|
||||
|
||||
Categories
|
||||
----------
|
||||
|
||||
- [Extension][32]
|
||||
- [Integration][2]
|
||||
- [Theme][3]
|
||||
|
||||
|
||||
Extension
|
||||
---------
|
||||
|
||||
- [**comex.xplr**][21] One xplr plugin to compress and extract them all.
|
||||
- [**command-mode.xplr**][37] The missing command mode for xplr.
|
||||
- [**type-to-nav.xplr**][28] Inspired by [nnn's type-to-nav mode][29] for xplr,
|
||||
- [**sayanarijit/command-mode.xplr**][37] The missing command mode for xplr.
|
||||
- [**igorepst/context-switch.xplr**][42] Context switch plugin for xplr.
|
||||
- [**sayanarijit/dual-pane.xplr**][43] Implements support for dual-pane navigation into xplr.
|
||||
- [**sayanarijit/map.xplr**][38] Visually inspect and interactively execute batch commands using xplr.
|
||||
- [**sayanarijit/offline-docs.xplr**][51] Fetch the appropriate version of xplr docs and browse offline.
|
||||
- [**sayanarijit/regex-search.xplr**][55] Bring back the regex based search in xplr.
|
||||
- [**sayanarijit/registers.xplr**][49] Use multiple registers to store the selected paths.
|
||||
- [**sayanarijit/tree-view.xplr**][61] Hackable tree view for xplr
|
||||
- [**sayanarijit/tri-pane.xplr**][56] xplr plugin that implements ranger-like three pane layout.
|
||||
- [**sayanarijit/type-to-nav.xplr**][28] Inspired by [nnn's type-to-nav mode][29] for xplr,
|
||||
with some tweaks.
|
||||
- [**dtomvan/term.xplr**][39] Terminal integration for xplr.
|
||||
- [**sayanarijit/wl-clipboard.xplr**][52] Copy and paste with system clipboard using wl-clipboard
|
||||
- [**dtomvan/xpm.xplr**][47] The XPLR Plugin Manager.
|
||||
- [**emsquid/style.xplr**][57] Helper plugin that allows you to integrate xplr's [Style][58] anywhere.
|
||||
|
||||
### Integration
|
||||
|
||||
Integration
|
||||
-----------
|
||||
|
||||
- [**alacritty.xplr**][33] [Alacritty][34] integration for xplr.
|
||||
- [**dragon.xplr**][4] Drag and drop files using [dragon][5].
|
||||
- [**dua-cli.xplr**][6] Get the disk usage using [dua-cli][7] with selection
|
||||
- [**sayanarijit/alacritty.xplr**][33] [Alacritty][34] integration for xplr.
|
||||
- [**sayanarijit/dragon.xplr**][4] Drag and drop files using [dragon][5].
|
||||
- [**sayanarijit/dua-cli.xplr**][6] Get the disk usage using [dua-cli][7] with selection
|
||||
support.
|
||||
- [**fzf.xplr**][8] Fuzzy search using [fzf][9] to focus on a file or enter
|
||||
into a directory.
|
||||
- [**nvim-ctrl.xplr**][35] Send files to running Neovim sessions using
|
||||
- [**sayanarijit/fzf.xplr**][8] Fuzzy search using [fzf][9] to focus on a file or enter.
|
||||
- [**sayanarijit/find.xplr**][44] An interactive finder plugin to complement [map.xplr][38].
|
||||
- [**Junker/nuke.xplr**][53] Open files in apps by file type or mime.
|
||||
- [**sayanarijit/nvim-ctrl.xplr**][35] Send files to running Neovim sessions using
|
||||
[nvim-ctrl][36].
|
||||
- [**paste-rs.xplr**][23] Use this plugin to paste your files to
|
||||
- [**dtomvan/ouch.xplr**][40] This plugin uses [ouch][41] to compress and decompress files.
|
||||
- [**dtomvan/paste-rs.xplr**][23] Use this plugin to paste your files to
|
||||
[paste.rs][24], and open/delete them later using [fzf][9].
|
||||
- [**preview-tabbed.xplr**][10] Preview paths using suckless [tabbed][11] and
|
||||
- [**sayanarijit/preview-tabbed.xplr**][10] Preview paths using suckless [tabbed][11] and
|
||||
[nnn preview-tabbed][12].
|
||||
- [**qrcp.xplr**][26] Send and receive files via QR code using [qrcp][27].
|
||||
- [**trash-cli.xplr**][13] Trash files and directories using [trash-cli][14].
|
||||
- [**xargs.xplr**][22] Batch execute commands on the focused or selected files
|
||||
using `xargs`.
|
||||
- [**xclip.xplr**][15] Copy and paste with system clipboard using [xclip][16].
|
||||
- [**zoxide.xplr**][17] Change directory using the [zoxide][18] database.
|
||||
- [**sayanarijit/qrcp.xplr**][26] Send and receive files via QR code using [qrcp][27].
|
||||
- [**sayanarijit/scp.xplr**][54] Integrate xplr with scp.
|
||||
- [**sayanarijit/trash-cli.xplr**][13] Trash files and directories using [trash-cli][14].
|
||||
- [**sayanarijit/xclip.xplr**][15] Copy and paste with system clipboard using [xclip][16].
|
||||
- [**sayanarijit/zoxide.xplr**][17] Change directory using the [zoxide][18] database.
|
||||
|
||||
### Theme
|
||||
|
||||
Theme
|
||||
-----
|
||||
- [**sayanarijit/material-landscape.xplr**][19] Material Landscape
|
||||
- [**sayanarijit/material-landscape2.xplr**][20] Material Landscape 2
|
||||
- [**sayanarijit/zentable.xplr**][31] A clean, distraction free xplr table UI
|
||||
- [**dy-sh/dysh-style.xplr**][63] Complements xplr theme with icons and highlighting.
|
||||
- [**prncss-xyz/icons.xplr**][30] An icon theme for xplr.
|
||||
- [**dtomvan/extra-icons.xplr**][50] Adds more icons to icons.xplr, compatible
|
||||
with zentable.xplr.
|
||||
- [**hartan/web-devicons.xplr**][59] Adds [nvim-web-devicons][60] to xplr with
|
||||
optional coloring
|
||||
- [**duganchen/one-table-column.xplr**][62] Moves file stats to a status bar.
|
||||
- [**dy-sh/get-rid-of-index.xplr**][64] Removes the index column.
|
||||
|
||||
- [**material-landscape.xplr**][19] Material Landscape
|
||||
- [**material-landscape2.xplr**][20] Material Landscape 2
|
||||
- [**zentable.xplr**][31] A clean, distraction free xplr table UI
|
||||
- [**icons.xplr**][30] An icon theme for xplr.
|
||||
## Also See:
|
||||
|
||||
- [Awesome Hacks][45]
|
||||
- [Awesome Integrations][46]
|
||||
|
||||
[1]:./writing-plugins.md
|
||||
[2]:#integration
|
||||
[3]:#theme
|
||||
[4]:https://github.com/sayanarijit/dragon.xplr
|
||||
[5]:https://github.com/mwh/dragon
|
||||
[6]:https://github.com/sayanarijit/dua-cli.xplr
|
||||
[7]:https://github.com/Byron/dua-cli
|
||||
[8]:https://github.com/sayanarijit/fzf.xplr
|
||||
[9]:https://github.com/junegunn/fzf
|
||||
[10]:https://github.com/sayanarijit/preview-tabbed.xplr
|
||||
[11]:https://tools.suckless.org/tabbed/
|
||||
[12]:https://github.com/jarun/nnn/blob/master/plugins/preview-tabbed
|
||||
[13]:https://github.com/sayanarijit/trash-cli.xplr
|
||||
[14]:https://github.com/andreafrancia/trash-cli
|
||||
[15]:https://github.com/sayanarijit/xclip.xplr
|
||||
[16]:https://github.com/astrand/xclip
|
||||
[17]:https://github.com/sayanarijit/zoxide.xplr
|
||||
[18]:https://github.com/ajeetdsouza/zoxide
|
||||
[19]:https://github.com/sayanarijit/material-landscape.xplr
|
||||
[20]:https://github.com/sayanarijit/material-landscape2.xplr
|
||||
[21]:https://github.com/sayanarijit/comex.xplr
|
||||
[22]:https://github.com/sayanarijit/xargs.xplr
|
||||
[23]:https://github.com/dtomvan/paste-rs.xplr
|
||||
[24]:https://paste.rs
|
||||
[25]:https://github.com/sayanarijit/completion.xplr
|
||||
[26]:https://github.com/sayanarijit/qrcp.xplr
|
||||
[27]:https://github.com/claudiodangelis/qrcp
|
||||
[28]:https://github.com/prncss-xyz/type-to-nav.xplr
|
||||
[29]:https://github.com/jarun/nnn/wiki/concepts#type-to-nav
|
||||
[30]:https://github.com/prncss-xyz/icons.xplr
|
||||
[31]:https://github.com/sayanarijit/zentable.xplr
|
||||
[32]:#extension
|
||||
[33]:https://github.com/sayanarijit/alacritty.xplr
|
||||
[34]:https://github.com/alacritty/alacritty
|
||||
[35]:https://github.com/sayanarijit/nvim-ctrl.xplr
|
||||
[36]:https://github.com/chmln/nvim-ctrl
|
||||
[37]:https://github.com/sayanarijit/command-mode.xplr
|
||||
[1]: writing-plugins.md
|
||||
[2]: #integration
|
||||
[3]: #theme
|
||||
[4]: https://github.com/sayanarijit/dragon.xplr
|
||||
[5]: https://github.com/mwh/dragon
|
||||
[6]: https://github.com/sayanarijit/dua-cli.xplr
|
||||
[7]: https://github.com/Byron/dua-cli
|
||||
[8]: https://github.com/sayanarijit/fzf.xplr
|
||||
[9]: https://github.com/junegunn/fzf
|
||||
[10]: https://github.com/sayanarijit/preview-tabbed.xplr
|
||||
[11]: https://tools.suckless.org/tabbed/
|
||||
[12]: https://github.com/jarun/nnn/blob/master/plugins/preview-tabbed
|
||||
[13]: https://github.com/sayanarijit/trash-cli.xplr
|
||||
[14]: https://github.com/andreafrancia/trash-cli
|
||||
[15]: https://github.com/sayanarijit/xclip.xplr
|
||||
[16]: https://github.com/astrand/xclip
|
||||
[17]: https://github.com/sayanarijit/zoxide.xplr
|
||||
[18]: https://github.com/ajeetdsouza/zoxide
|
||||
[19]: https://github.com/sayanarijit/material-landscape.xplr
|
||||
[20]: https://github.com/sayanarijit/material-landscape2.xplr
|
||||
[22]: https://github.com/sayanarijit/xargs.xplr
|
||||
[23]: https://github.com/dtomvan/paste-rs.xplr
|
||||
[24]: https://paste.rs
|
||||
[25]: https://github.com/sayanarijit/completion.xplr
|
||||
[26]: https://github.com/sayanarijit/qrcp.xplr
|
||||
[27]: https://github.com/claudiodangelis/qrcp
|
||||
[28]: https://github.com/sayanarijit/type-to-nav.xplr
|
||||
[29]: https://github.com/jarun/nnn/wiki/concepts#type-to-nav
|
||||
[30]: https://github.com/prncss-xyz/icons.xplr
|
||||
[31]: https://github.com/sayanarijit/zentable.xplr
|
||||
[32]: #extension
|
||||
[33]: https://github.com/sayanarijit/alacritty.xplr
|
||||
[34]: https://github.com/alacritty/alacritty
|
||||
[35]: https://github.com/sayanarijit/nvim-ctrl.xplr
|
||||
[36]: https://github.com/chmln/nvim-ctrl
|
||||
[37]: https://github.com/sayanarijit/command-mode.xplr
|
||||
[38]: https://github.com/sayanarijit/map.xplr
|
||||
[39]: https://github.com/dtomvan/term.xplr
|
||||
[40]: https://github.com/dtomvan/ouch.xplr
|
||||
[41]: https://github.com/ouch-org/ouch
|
||||
[42]: https://github.com/igorepst/context-switch.xplr
|
||||
[43]: https://github.com/sayanarijit/dual-pane.xplr
|
||||
[44]: https://github.com/sayanarijit/find.xplr
|
||||
[45]: awesome-hacks.md
|
||||
[46]: awesome-integrations.md
|
||||
[47]: https://github.com/dtomvan/xpm.xplr
|
||||
[48]: installing-plugins.md
|
||||
[49]: https://github.com/sayanarijit/registers.xplr
|
||||
[50]: https://github.com/dtomvan/extra-icons.xplr
|
||||
[51]: https://github.com/sayanarijit/offline-docs.xplr
|
||||
[52]: https://github.com/sayanarijit/wl-clipboard.xplr
|
||||
[53]: https://github.com/Junker/nuke.xplr
|
||||
[54]: https://github.com/sayanarijit/scp.xplr
|
||||
[55]: https://github.com/sayanarijit/regex-search.xplr
|
||||
[56]: https://github.com/sayanarijit/tri-pane.xplr
|
||||
[57]: https://github.com/emsquid/style.xplr
|
||||
[58]: style.md
|
||||
[59]: https://gitlab.com/hartan/web-devicons.xplr
|
||||
[60]: https://github.com/nvim-tree/nvim-web-devicons
|
||||
[61]: https://github.com/sayanarijit/tree-view.xplr
|
||||
[62]: https://github.com/duganchen/one-table-column.xplr
|
||||
[63]: https://github.com/dy-sh/dysh-style.xplr
|
||||
[64]: https://github.com/dy-sh/get-rid-of-index.xplr
|
||||
|
@ -0,0 +1,37 @@
|
||||
# Borders
|
||||
|
||||
xplr allows customizing the shape and style of the borders.
|
||||
|
||||
### Border
|
||||
|
||||
A border is a [sum type][2] that can be one of the following:
|
||||
|
||||
- "Top"
|
||||
- "Right"
|
||||
- "Bottom"
|
||||
- "Left"
|
||||
|
||||
### Border Type
|
||||
|
||||
A border type is a [sum type][2] that can be one of the following:
|
||||
|
||||
- "Plain"
|
||||
- "Rounded"
|
||||
- "Double"
|
||||
- "Thick"
|
||||
|
||||
### Border Style
|
||||
|
||||
The [style][1] of the borders.
|
||||
|
||||
## Example
|
||||
|
||||
```lua
|
||||
xplr.config.general.panel_ui.default.borders = { "Top", "Right", "Bottom", "Left" }
|
||||
xplr.config.general.panel_ui.default.border_type = "Thick"
|
||||
xplr.config.general.panel_ui.default.border_style.fg = "Black"
|
||||
xplr.config.general.panel_ui.default.border_style.bg = "Gray"
|
||||
```
|
||||
|
||||
[1]: style.md#style
|
||||
[2]: sum-type.md
|
@ -1,15 +0,0 @@
|
||||
Community
|
||||
=========
|
||||
|
||||
Building an active community of awesome people and learning stuff together is
|
||||
one of my reasons to publish this tool and maintain it. Hence, please feel free
|
||||
to reach out via your preferred way.
|
||||
|
||||
- Real-time chat lovers can [**join our discord channel**][1].
|
||||
- Forum discussion veterans can [**start a new GitHub discussion**][2].
|
||||
|
||||
BTW I like Miyazaki and Shinkai works.
|
||||
|
||||
|
||||
[1]:https://discord.gg/JmasSPCcz3
|
||||
[2]:https://github.com/sayanarijit/xplr/discussions
|
@ -0,0 +1,30 @@
|
||||
# Concept
|
||||
|
||||
These are the concepts that make xplr probably the most hackable terminal file
|
||||
explorer.
|
||||
|
||||
- [Key Bindings][1]
|
||||
- [Node Type][2]
|
||||
- [Layout][3]
|
||||
- [Mode][4]
|
||||
- [Message][5]
|
||||
- [Borders][6]
|
||||
- [Style][7]
|
||||
- [Sorting][8]
|
||||
- [Filtering][9]
|
||||
- [Column Renderer][10]
|
||||
- [Lua Function Calls][11]
|
||||
- [Environment Variables and Pipes][12]
|
||||
|
||||
[1]: key-bindings.md
|
||||
[2]: node-type.md
|
||||
[3]: layout.md
|
||||
[4]: mode.md
|
||||
[5]: message.md
|
||||
[6]: borders.md
|
||||
[7]: style.md
|
||||
[8]: sorting.md
|
||||
[9]: filtering.md
|
||||
[10]: column-renderer.md
|
||||
[11]: lua-function-calls.md
|
||||
[12]: environment-variables-and-pipes.md
|
@ -1,47 +1,156 @@
|
||||
Configuration
|
||||
=============
|
||||
# Configuration
|
||||
|
||||
xplr can be configured using [Lua][1] via a special file
|
||||
named `init.lua`
|
||||
([example][2]), which
|
||||
can be placed in `~/.config/xplr/` (user specific) or `/etc/xplr/` (global)
|
||||
depending on the use case.
|
||||
xplr can be configured using [Lua][1] via a special file named `init.lua`,
|
||||
which can be placed in `~/.config/xplr/` (local to user) or `/etc/xplr/`
|
||||
(global) depending on the use case.
|
||||
|
||||
When a user specific configuration is available, the global configuration file
|
||||
will be ignored.
|
||||
When xplr loads, it first executes the [built-in init.lua][2] to set the
|
||||
default values, which is then overwritten by another config file, if found
|
||||
using the following lookup order:
|
||||
|
||||
However, it's also possible to place the file anywhere, with any name and use
|
||||
the command-line argument `-c` / `--config` to specify its path explicitely. In
|
||||
that case, both `~/.config/xplr/init.lua` and `/etc/xplr/init.lua` will be
|
||||
ignored.
|
||||
1. `--config /path/to/init.lua`
|
||||
2. `~/.config/xplr/init.lua`
|
||||
3. `/etc/xplr/init.lua`
|
||||
|
||||
The first one found will be loaded by xplr and the lookup will stop.
|
||||
|
||||
How Config Is Loaded
|
||||
--------------------
|
||||
The loaded config can be further extended using the `-C` or `--extra-config`
|
||||
command-line option.
|
||||
|
||||
When xplr loads, it first executes the built-in
|
||||
[init.lua][2] to set
|
||||
the default values, which is then overwritten by another config file, if found
|
||||
using the following lookup order:
|
||||
[1]: https://www.lua.org
|
||||
[2]: https://github.com/sayanarijit/xplr/blob/main/src/init.lua
|
||||
[3]: https://xplr.dev/en/upgrade-guide
|
||||
|
||||
## Config
|
||||
|
||||
The xplr configuration, exposed via `xplr.config` Lua API contains the
|
||||
following sections.
|
||||
|
||||
See:
|
||||
|
||||
- [xplr.config.general](https://xplr.dev/en/general-config)
|
||||
- [xplr.config.node_types](https://xplr.dev/en/node_types)
|
||||
- [xplr.config.layouts](https://xplr.dev/en/layouts)
|
||||
- [xplr.config.modes](https://xplr.dev/en/modes)
|
||||
|
||||
## Function
|
||||
|
||||
While `xplr.config` defines all the static parts of the configuration,
|
||||
`xplr.fn` defines all the dynamic parts using functions.
|
||||
|
||||
See: [Lua Function Calls](https://xplr.dev/en/lua-function-calls)
|
||||
|
||||
As always, `xplr.fn.builtin` is where the built-in functions are defined
|
||||
that can be overwritten.
|
||||
|
||||
#### xplr.fn.builtin.try_complete_path
|
||||
|
||||
Tries to auto complete the path in the input buffer
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_table_row_cols_0
|
||||
|
||||
Renders the first column in the table
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_table_row_cols_1
|
||||
|
||||
Renders the second column in the table
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_table_row_cols_2
|
||||
|
||||
Renders the third column in the table
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_table_row_cols_3
|
||||
|
||||
Renders the fourth column in the table
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_table_row_cols_4
|
||||
|
||||
Renders the fifth column in the table
|
||||
|
||||
#### xplr.fn.custom
|
||||
|
||||
This is where the custom functions can be added.
|
||||
|
||||
There is currently no restriction on what kind of functions can be defined
|
||||
in `xplr.fn.custom`.
|
||||
|
||||
You can also use nested tables such as
|
||||
`xplr.fn.custom.my_plugin.my_function` to define custom functions.
|
||||
|
||||
## Hooks
|
||||
|
||||
This section of the configuration cannot be overwritten by another config
|
||||
file or plugin, since this is an optional lua return statement specific to
|
||||
each config file. It can be used to define things that should be explicit
|
||||
for reasons like performance concerns, such as hooks.
|
||||
|
||||
Plugins should expose the hooks, and require users to subscribe to them
|
||||
explicitly.
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
return {
|
||||
-- Add messages to send when the xplr loads.
|
||||
-- This is similar to the `--on-load` command-line option.
|
||||
--
|
||||
-- Type: list of [Message](https://xplr.dev/en/message#message)s
|
||||
on_load = {
|
||||
{ LogSuccess = "Configuration successfully loaded!" },
|
||||
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_load" },
|
||||
},
|
||||
|
||||
**--config /path/to/init.lua** > **~/.config/xplr/init.lua** > **/etc/xplr/init.lua**
|
||||
-- Add messages to send when the directory changes.
|
||||
--
|
||||
-- Type: list of [Message](https://xplr.dev/en/message#message)s
|
||||
on_directory_change = {
|
||||
{ LogSuccess = "Changed directory" },
|
||||
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_directory_change" },
|
||||
},
|
||||
|
||||
-- Add messages to send when the focus changes.
|
||||
--
|
||||
-- Type: list of [Message](https://xplr.dev/en/message#message)s
|
||||
on_focus_change = {
|
||||
{ LogSuccess = "Changed focus" },
|
||||
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_focus_change" },
|
||||
}
|
||||
|
||||
config
|
||||
------
|
||||
-- Add messages to send when the mode is switched.
|
||||
--
|
||||
-- Type: list of [Message](https://xplr.dev/en/message#message)s
|
||||
on_mode_switch = {
|
||||
{ LogSuccess = "Switched mode" },
|
||||
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_mode_switch" },
|
||||
}
|
||||
|
||||
The xplr configuration, exposed as `xplr.config` Lua API contains the
|
||||
following fields:
|
||||
-- Add messages to send when the layout is switched
|
||||
--
|
||||
-- Type: list of [Message](https://xplr.dev/en/message#message)s
|
||||
on_layout_switch = {
|
||||
{ LogSuccess = "Switched layout" },
|
||||
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_layout_switch" },
|
||||
}
|
||||
|
||||
- [general][3]
|
||||
- [modes][4]
|
||||
- [layouts][5]
|
||||
- [node_types][6]
|
||||
-- Add messages to send when the selection changes
|
||||
--
|
||||
-- Type: list of [Message](https://xplr.dev/en/message#message)s
|
||||
on_selection_change = {
|
||||
{ LogSuccess = "Selection changed" },
|
||||
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_selection_change" },
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[1]:https://www.lua.org/
|
||||
[2]:https://github.com/sayanarijit/xplr/blob/main/src/init.lua
|
||||
[3]:general-config.md
|
||||
[4]:modes.md
|
||||
[5]:layouts.md
|
||||
[6]:node_types.md
|
||||
> Note:
|
||||
>
|
||||
> It's not recommended to copy the entire configuration, unless you want to
|
||||
> freeze it and miss out on useful updates to the defaults.
|
||||
>
|
||||
> Instead, you can use this as a reference to overwrite only the parts you
|
||||
> want to update.
|
||||
>
|
||||
> If you still want to copy the entire configuration, make sure to put your
|
||||
> customization before the return statement.
|
||||
|
@ -0,0 +1,233 @@
|
||||
# Configure Key Bindings
|
||||
|
||||
In xplr, each keyboard input passes through a bunch of handlers (e.g. `on_key`,
|
||||
`on_number`, `default` etc.) in a given order. If any of the handlers is
|
||||
configured to with an [action][16], it will intercept the key and produce
|
||||
[messages][18] for xplr to handle.
|
||||
|
||||
Try [debug key bindings][31] to understand how key bindings actually work.
|
||||
|
||||
## Key Bindings
|
||||
|
||||
Key bindings contains the following information:
|
||||
|
||||
- [on_key][10]
|
||||
- [on_alphabet][11]
|
||||
- [on_number][12]
|
||||
- [on_alphanumeric][32]
|
||||
- [on_special_character][13]
|
||||
- [on_character][33]
|
||||
- [on_navigation][34]
|
||||
- [on_function][35]
|
||||
- [default][14]
|
||||
|
||||
### on_key
|
||||
|
||||
Type: mapping of [Key][15] to nullable [Action][16]
|
||||
|
||||
Defines what to do when an exact key is pressed.
|
||||
|
||||
### on_alphabet
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
An action to perform if the keyboard input is an alphabet and is not mapped via
|
||||
the [on_key][10] field.
|
||||
|
||||
### on_number
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
An action to perform if the keyboard input is a number and is not mapped via
|
||||
the [on_key][10] field.
|
||||
|
||||
### on_alphanumeric
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
An action to perform if the keyboard input is alphanumeric and is not mapped
|
||||
via the [on_key][10], [on_alphabet][11] or [on_number][12] field.
|
||||
|
||||
### on_special_character
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
An action to perform if the keyboard input is a special character and is not
|
||||
mapped via the [on_key][10] field.
|
||||
|
||||
### on_character
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
An action to perform if the keyboard input is a character and is not mapped
|
||||
via the [on_key][10], [on_alphabet][11], [on_number][12], [on_alphanumeric][32]
|
||||
or [on_special_character][13] field.
|
||||
|
||||
### on_navigation
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
An action to perform if the keyboard input is a navigation key and is not
|
||||
mapped via the [on_key][10] field.
|
||||
|
||||
### on_function
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
An action to perform if the keyboard input is a function key and is not mapped
|
||||
via the [on_key][10] field.
|
||||
|
||||
### default
|
||||
|
||||
Type: nullable [Action][16]
|
||||
|
||||
Default action to perform in case if a keyboard input not mapped via any of the
|
||||
`on_*` fields mentioned above.
|
||||
|
||||
## Key
|
||||
|
||||
A key is a [sum type][36] can be one of the following:
|
||||
|
||||
- 0, 1, ... 9
|
||||
- a, b, ... z
|
||||
- A, B, ... Z
|
||||
- f1, f2, ... f12
|
||||
- backspace
|
||||
- left
|
||||
- right
|
||||
- up
|
||||
- down
|
||||
- home
|
||||
- end
|
||||
- page-up
|
||||
- page-down
|
||||
- back-tab
|
||||
- delete
|
||||
- insert
|
||||
- enter
|
||||
- tab
|
||||
- esc
|
||||
- ctrl-a, ctrl-b, ... ctrl-z
|
||||
- ctrl-backspace, ctrl-left, ... ctrl-esc
|
||||
- alt-a, alt-b, ... alt-z
|
||||
|
||||
And finally, the special characters - including space (`" "`) with their `ctrl`
|
||||
bindings.
|
||||
|
||||
## Action
|
||||
|
||||
An action contains the following information:
|
||||
|
||||
- [help][1]
|
||||
- [messages][17]
|
||||
|
||||
### help
|
||||
|
||||
Type: nullable string
|
||||
|
||||
Description of what it does. If unspecified, it will be excluded from the help
|
||||
menu.
|
||||
|
||||
### messages
|
||||
|
||||
Type: A list of [Message][18] to send.
|
||||
|
||||
The list of messages to send when a key is pressed.
|
||||
|
||||
## Tutorial: Adding a New Mode
|
||||
|
||||
Assuming xplr is [installed][19] and [setup][20], let's
|
||||
add our own mode to integrate xplr with [fzf][21].
|
||||
|
||||
We'll call it `fzxplr` mode.
|
||||
|
||||
First, let's add a custom mode called `fzxplr`, and map the key `F` to an
|
||||
action that will call `fzf` to search and focus on a file or enter into a
|
||||
directory.
|
||||
|
||||
```lua
|
||||
xplr.config.modes.custom.fzxplr = {
|
||||
name = "fzxplr",
|
||||
key_bindings = {
|
||||
on_key = {
|
||||
F = {
|
||||
help = "search",
|
||||
messages = {
|
||||
{
|
||||
BashExec = [===[
|
||||
PTH=$(cat "${XPLR_PIPE_DIRECTORY_NODES_OUT:?}" | awk -F/ '{print $NF}' | fzf)
|
||||
if [ -d "$PTH" ]; then
|
||||
"$XPLR" -m 'ChangeDirectory: %q' "$PTH"
|
||||
else
|
||||
"$XPLR" -m 'FocusPath: %q' "$PTH"
|
||||
fi
|
||||
]===]
|
||||
},
|
||||
"PopMode",
|
||||
},
|
||||
},
|
||||
},
|
||||
default = {
|
||||
messages = {
|
||||
"PopMode",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, the key `F` in mode `fzxplr` (the name can be anything)
|
||||
executes a script in `bash`.
|
||||
|
||||
`BashExec`, `PopMode`, `SwitchModeBuiltin`, `ChangeDirectory` and `FocusPath`
|
||||
are [messages][18], `$XPLR`, `$XPLR_PIPE_DIRECTORY_NODES_OUT` are
|
||||
[environment variables][22] exported by `xplr` before executing the command.
|
||||
They contain the path to the [input][23] and [output][24] pipes that allows
|
||||
external tools to interact with `xplr`.
|
||||
|
||||
Now that we have our new mode ready, let's add an entry point to this mode via
|
||||
the `default` mode.
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key["F"] = {
|
||||
help = "fzf mode",
|
||||
messages = {
|
||||
{ SwitchModeCustom = "fzxplr" },
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Now let's try out the new `xplr`-`fzf` integration.
|
||||
|
||||
[![xplr-fzf.gif][25]][26]
|
||||
|
||||
---
|
||||
|
||||
Visit [Awesome Plugins][27] for more [integration][28] options.
|
||||
|
||||
[1]: #help
|
||||
[10]: #on_key
|
||||
[11]: #on_alphabet
|
||||
[12]: #on_number
|
||||
[13]: #on_special_character
|
||||
[14]: #default
|
||||
[15]: #key
|
||||
[16]: #action
|
||||
[17]: #messages
|
||||
[18]: message.md#message
|
||||
[19]: install.md
|
||||
[20]: post-install.md
|
||||
[21]: https://github.com/junegunn/fzf
|
||||
[22]: environment-variables-and-pipes.md#environment-variables
|
||||
[23]: environment-variables-and-pipes.md#input-pipe
|
||||
[24]: environment-variables-and-pipes.md#output-pipes
|
||||
[25]: https://s3.gifyu.com/images/xplr-fzf.gif
|
||||
[26]: https://gifyu.com/image/tW86
|
||||
[27]: awesome-plugins.md
|
||||
[28]: awesome-plugins.md#integration
|
||||
[31]: debug-key-bindings.md
|
||||
[32]: #on_alphanumeric
|
||||
[33]: #on_character
|
||||
[34]: #on_navigation
|
||||
[35]: #on_function
|
||||
[36]: sum-type.md
|
@ -1,25 +0,0 @@
|
||||
If you like xplr, and want to contribute, that would be really awesome.
|
||||
|
||||
You can contribute to this project in the following ways
|
||||
|
||||
- Contribute your time and expertise (read [CONTRIBUTING.md][1] for instructions).
|
||||
- **Developers:** You can help me improve my code, fix things, implement features etc.
|
||||
- **Repository maintainers:** You can save the users from the pain of managing xplr in their system manually.
|
||||
- **Code Reviewers:** Teach me your ways of code.
|
||||
- **Designers:** You can make the logo even more awesome, donate stickers and blog post worthy pictures.
|
||||
- **Bloggers, YouTubers & broadcasters:** You can help spread the word.
|
||||
|
||||
|
||||
- Contribute by donating.
|
||||
- You can [fuel me with coins of encouragement][2] or [buy me a coffee][3].
|
||||
|
||||
For further queries or concern related to `xplr`, [just ask us][4].
|
||||
|
||||
### Backers
|
||||
<a href="https://opencollective.com/xplr#backer"><img src="https://opencollective.com/xplr/tiers/backer.svg?width=890" /></a>
|
||||
|
||||
|
||||
[1]:https://github.com/sayanarijit/xplr/blob/main/CONTRIBUTING.md
|
||||
[2]:https://opencollective.com/xplr
|
||||
[3]:https://ko-fi.com/sayanarijit
|
||||
[4]:community.md
|
@ -0,0 +1,109 @@
|
||||
# Debug Key Bindings
|
||||
|
||||
If you need help debugging or understanding key bindings DYI way, you can
|
||||
create a `test.lua` file with the following script, launch xplr with
|
||||
`xplr --extra-config test.lua`, press `#` and play around.
|
||||
|
||||
```lua
|
||||
-- The global key bindings inherited by all the modes.
|
||||
xplr.config.general.global_key_bindings = {
|
||||
on_key = {
|
||||
esc = {
|
||||
help = "escape",
|
||||
messages = {
|
||||
{ LogInfo = "global on_key(esc) called" },
|
||||
"PopMode",
|
||||
},
|
||||
},
|
||||
["ctrl-c"] = {
|
||||
help = "terminate",
|
||||
messages = {
|
||||
"Terminate",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
-- Press `#` to enter the `debug key bindings` mode.
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key["#"] = {
|
||||
help = "test",
|
||||
messages = {
|
||||
"PopMode",
|
||||
{ SwitchModeCustom = "debug_key_bindings" },
|
||||
},
|
||||
}
|
||||
|
||||
-- The `debug key bindings` mode.
|
||||
xplr.config.modes.custom.debug_key_bindings = {
|
||||
name = "debug key bindings",
|
||||
key_bindings = {
|
||||
on_key = {
|
||||
["1"] = {
|
||||
messages = {
|
||||
{ LogInfo = "on_key(1) called" },
|
||||
},
|
||||
},
|
||||
a = {
|
||||
messages = {
|
||||
{ LogInfo = "on_key(a) called" },
|
||||
},
|
||||
},
|
||||
["`"] = {
|
||||
messages = {
|
||||
{ LogInfo = "on_key(`) called" },
|
||||
},
|
||||
},
|
||||
tab = {
|
||||
messages = {
|
||||
{ LogInfo = "on_key(tab) called" },
|
||||
},
|
||||
},
|
||||
f1 = {
|
||||
messages = {
|
||||
{ LogInfo = "on_key(f1) called" },
|
||||
},
|
||||
},
|
||||
},
|
||||
on_alphabet = {
|
||||
messages = {
|
||||
{ LogInfo = "on_alphabet called" },
|
||||
},
|
||||
},
|
||||
on_number = {
|
||||
messages = {
|
||||
{ LogInfo = "on_number called" },
|
||||
},
|
||||
},
|
||||
-- on_alphanumeric = {
|
||||
-- messages = {
|
||||
-- { LogInfo = "on_alphanumeric called" },
|
||||
-- },
|
||||
-- },
|
||||
on_special_character = {
|
||||
messages = {
|
||||
{ LogInfo = "on_special_character called" },
|
||||
},
|
||||
},
|
||||
-- on_character = {
|
||||
-- messages = {
|
||||
-- { LogInfo = "on_character called" },
|
||||
-- },
|
||||
-- },
|
||||
on_navigation = {
|
||||
messages = {
|
||||
{ LogInfo = "on_navigation called" },
|
||||
},
|
||||
},
|
||||
on_function = {
|
||||
messages = {
|
||||
{ LogInfo = "on_function called" },
|
||||
},
|
||||
},
|
||||
default = {
|
||||
messages = {
|
||||
{ LogInfo = "default called" },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
@ -1,264 +1,348 @@
|
||||
Default Key Bindings
|
||||
====================
|
||||
# Default Key Bindings
|
||||
|
||||
The default key binding is inspired by [vim][1] and slightly
|
||||
overlaps with [nnn][2], but it's supposed to be
|
||||
customized as per user requirements.
|
||||
overlaps with [nnn][2], but it's supposed to be customized as per user
|
||||
requirements.
|
||||
|
||||
When you press `?` in [default mode][3], you can see the complete list
|
||||
of [modes][4] and the key mappings for each mode.
|
||||
|
||||
[1]: https://www.vim.org/
|
||||
[2]: https://github.com/jarun/nnn/
|
||||
[3]: #default
|
||||
[4]: modes.md
|
||||
|
||||
### default
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
. | | show hidden
|
||||
/ | ctrl-f | search
|
||||
: | | action
|
||||
? | | global help menu
|
||||
G | | go to bottom
|
||||
V | ctrl-a | select/unselect all
|
||||
ctrl-c | | terminate
|
||||
ctrl-i | tab | next visited path
|
||||
ctrl-o | | last visited path
|
||||
ctrl-r | | refresh screen
|
||||
ctrl-u | | clear selection
|
||||
ctrl-w | | switch layout
|
||||
d | | delete
|
||||
down | j | down
|
||||
enter | | quit with result
|
||||
f | | filter
|
||||
g | | go to
|
||||
h | left | back
|
||||
k | up | up
|
||||
l | right | enter
|
||||
q | | quit
|
||||
r | | rename
|
||||
s | | sort
|
||||
space | v | toggle selection
|
||||
~ | | go home
|
||||
[0-9] | | input
|
||||
| key | remaps | action |
|
||||
| --------- | ------ | ------------------- |
|
||||
| ( | | prev deep branch |
|
||||
| ) | | next deep branch |
|
||||
| . | | show hidden |
|
||||
| / | ctrl-f | search |
|
||||
| : | | action |
|
||||
| ? | f1 | global help menu |
|
||||
| G | | go to bottom |
|
||||
| V | ctrl-a | select/unselect all |
|
||||
| c | | copy to |
|
||||
| ctrl-d | | duplicate as |
|
||||
| ctrl-i | tab | next visited path |
|
||||
| ctrl-n | | next selection |
|
||||
| ctrl-o | | last visited path |
|
||||
| ctrl-p | | prev selection |
|
||||
| ctrl-r | | refresh screen |
|
||||
| ctrl-u | | clear selection |
|
||||
| ctrl-w | | switch layout |
|
||||
| d | | delete |
|
||||
| down | j | down |
|
||||
| enter | | quit with result |
|
||||
| f | | filter |
|
||||
| g | | go to |
|
||||
| h | left | back |
|
||||
| k | up | up |
|
||||
| l | right | enter |
|
||||
| m | | move to |
|
||||
| page-down | | scroll down |
|
||||
| page-up | | scroll up |
|
||||
| q | | quit |
|
||||
| r | | rename |
|
||||
| s | | sort |
|
||||
| space | v | toggle selection |
|
||||
| { | | scroll up half |
|
||||
| } | | scroll down half |
|
||||
| ~ | | go home |
|
||||
| [0-9] | | input |
|
||||
|
||||
### go_to_path
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| tab | | try complete |
|
||||
|
||||
### rename
|
||||
|
||||
### recover
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
ctrl-c | | terminate
|
||||
esc | | escape
|
||||
|
||||
|
||||
### filter
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
R | | relative does not contain
|
||||
backspace | | remove last filter
|
||||
ctrl-c | | terminate
|
||||
ctrl-r | | reset filters
|
||||
ctrl-u | | clear filters
|
||||
enter | esc | done
|
||||
r | | relative does contain
|
||||
|
||||
|
||||
### number
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
backspace | | remove last character
|
||||
ctrl-c | | terminate
|
||||
ctrl-u | | remove line
|
||||
ctrl-w | | remove last word
|
||||
down | j | to down
|
||||
enter | | to index
|
||||
esc | | cancel
|
||||
k | up | to up
|
||||
[0-9] | | input
|
||||
|
||||
|
||||
### go to
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
ctrl-c | | terminate
|
||||
esc | | cancel
|
||||
f | | follow symlink
|
||||
g | | top
|
||||
x | | open in gui
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| tab | | try complete |
|
||||
|
||||
### recover
|
||||
|
||||
### search
|
||||
| key | remaps | action |
|
||||
| --- | ------ | ---------------- |
|
||||
| f1 | | global help menu |
|
||||
|
||||
### go_to
|
||||
|
||||
| key | remaps | action |
|
||||
| --- | ------ | ---------------- |
|
||||
| f | | follow symlink |
|
||||
| f1 | | global help menu |
|
||||
| g | | top |
|
||||
| i | | initial $PWD |
|
||||
| p | | path |
|
||||
| x | | open in gui |
|
||||
|
||||
### relative_path_does_match_regex
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
|
||||
### action
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | -------------------- |
|
||||
| ! | | shell |
|
||||
| c | | create |
|
||||
| e | | open in editor |
|
||||
| f1 | | global help menu |
|
||||
| l | | logs |
|
||||
| m | | toggle mouse |
|
||||
| p | | edit permissions |
|
||||
| q | | quit options |
|
||||
| s | | selection operations |
|
||||
| v | | vroot |
|
||||
| [0-9] | | go to index |
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
backspace | | remove last character
|
||||
ctrl-c | | terminate
|
||||
ctrl-n | down | down
|
||||
ctrl-p | up | up
|
||||
ctrl-u | | remove line
|
||||
ctrl-w | | remove last word
|
||||
enter | esc | focus
|
||||
left | | back
|
||||
right | | enter
|
||||
tab | | toggle selection
|
||||
|
||||
|
||||
### selection ops
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
c | | copy here
|
||||
ctrl-c | | terminate
|
||||
esc | | cancel
|
||||
m | | move here
|
||||
x | | open in gui
|
||||
|
||||
|
||||
### action to
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
! | | shell
|
||||
c | | create
|
||||
ctrl-c | | terminate
|
||||
e | | open in editor
|
||||
esc | | cancel
|
||||
l | | logs
|
||||
m | | toggle mouse
|
||||
q | | quit options
|
||||
s | | selection operations
|
||||
[0-9] | | go to index
|
||||
### default
|
||||
|
||||
| key | remaps | action |
|
||||
| --------- | ------ | ------------------- |
|
||||
| ( | | prev deep branch |
|
||||
| ) | | next deep branch |
|
||||
| . | | show hidden |
|
||||
| / | ctrl-f | search |
|
||||
| : | | action |
|
||||
| ? | f1 | global help menu |
|
||||
| G | | go to bottom |
|
||||
| V | ctrl-a | select/unselect all |
|
||||
| c | | copy to |
|
||||
| ctrl-d | | duplicate as |
|
||||
| ctrl-i | tab | next visited path |
|
||||
| ctrl-n | | next selection |
|
||||
| ctrl-o | | last visited path |
|
||||
| ctrl-p | | prev selection |
|
||||
| ctrl-r | | refresh screen |
|
||||
| ctrl-u | | clear selection |
|
||||
| ctrl-w | | switch layout |
|
||||
| d | | delete |
|
||||
| down | j | down |
|
||||
| enter | | quit with result |
|
||||
| f | | filter |
|
||||
| g | | go to |
|
||||
| h | left | back |
|
||||
| k | up | up |
|
||||
| l | right | enter |
|
||||
| m | | move to |
|
||||
| page-down | | scroll down |
|
||||
| page-up | | scroll up |
|
||||
| q | | quit |
|
||||
| r | | rename |
|
||||
| s | | sort |
|
||||
| space | v | toggle selection |
|
||||
| { | | scroll up half |
|
||||
| } | | scroll down half |
|
||||
| ~ | | go home |
|
||||
| [0-9] | | input |
|
||||
|
||||
### debug_error
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ------------------- |
|
||||
| enter | | open logs in editor |
|
||||
| f1 | | global help menu |
|
||||
| q | | quit |
|
||||
|
||||
### create_directory
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| tab | | try complete |
|
||||
|
||||
### selection_ops
|
||||
|
||||
| key | remaps | action |
|
||||
| --- | ------ | ---------------- |
|
||||
| c | | copy here |
|
||||
| e | | edit selection |
|
||||
| f1 | | global help menu |
|
||||
| h | | hardlink here |
|
||||
| l | | list selection |
|
||||
| m | | move here |
|
||||
| s | | softlink here |
|
||||
| u | | clear selection |
|
||||
|
||||
### relative_path_does_not_match_regex
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
|
||||
### create_file
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| tab | | try complete |
|
||||
|
||||
### quit
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ----------------------- |
|
||||
| enter | | just quit |
|
||||
| f | | quit printing focus |
|
||||
| f1 | | global help menu |
|
||||
| p | | quit printing pwd |
|
||||
| r | | quit printing result |
|
||||
| s | | quit printing selection |
|
||||
|
||||
### create
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
ctrl-c | | terminate
|
||||
d | | create directory
|
||||
esc | | cancel
|
||||
f | | create file
|
||||
| key | remaps | action |
|
||||
| --- | ------ | ---------------- |
|
||||
| d | | create directory |
|
||||
| f | | create file |
|
||||
| f1 | | global help menu |
|
||||
|
||||
### vroot
|
||||
|
||||
### create file
|
||||
| key | remaps | action |
|
||||
| ------ | ------ | ---------------- |
|
||||
| . | | vroot $PWD |
|
||||
| / | | vroot / |
|
||||
| ctrl-r | | reset vroot |
|
||||
| ctrl-u | | unset vroot |
|
||||
| f1 | | global help menu |
|
||||
| v | | toggle vroot |
|
||||
| ~ | | vroot $HOME |
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
backspace | | remove last character
|
||||
ctrl-c | | terminate
|
||||
ctrl-u | | remove line
|
||||
ctrl-w | | remove last word
|
||||
enter | | create file
|
||||
esc | | cancel
|
||||
|
||||
|
||||
### create directory
|
||||
### search
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
backspace | | remove last character
|
||||
ctrl-c | | terminate
|
||||
ctrl-u | | remove line
|
||||
ctrl-w | | remove last word
|
||||
enter | | create directory
|
||||
esc | | cancel
|
||||
| key | remaps | action |
|
||||
| ------ | ------ | ----------------------- |
|
||||
| ctrl-a | | toggle search algorithm |
|
||||
| ctrl-f | | fuzzy search |
|
||||
| ctrl-n | down | down |
|
||||
| ctrl-p | up | up |
|
||||
| ctrl-r | | regex search |
|
||||
| ctrl-s | | sort (no search order) |
|
||||
| ctrl-z | | toggle ordering |
|
||||
| enter | | submit |
|
||||
| esc | | cancel |
|
||||
| f1 | | global help menu |
|
||||
| left | | back |
|
||||
| right | | enter |
|
||||
| tab | | toggle selection |
|
||||
|
||||
### switch_layout
|
||||
|
||||
| key | remaps | action |
|
||||
| --- | ------ | -------------------- |
|
||||
| 1 | | default |
|
||||
| 2 | | no help menu |
|
||||
| 3 | | no selection panel |
|
||||
| 4 | | no help or selection |
|
||||
| f1 | | global help menu |
|
||||
|
||||
### sort
|
||||
|
||||
### rename
|
||||
| key | remaps | action |
|
||||
| --------- | ------ | --------------------------------- |
|
||||
| ! | | reverse sorters |
|
||||
| C | | by created reverse |
|
||||
| E | | by canonical extension reverse |
|
||||
| L | | by last modified reverse |
|
||||
| M | | by canonical mime essence reverse |
|
||||
| N | | by node type reverse |
|
||||
| R | | by relative path reverse |
|
||||
| S | | by size reverse |
|
||||
| backspace | | remove last sorter |
|
||||
| c | | by created |
|
||||
| ctrl-r | | reset sorters |
|
||||
| ctrl-u | | clear sorters |
|
||||
| e | | by canonical extension |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| l | | by last modified |
|
||||
| m | | by canonical mime essence |
|
||||
| n | | by node type |
|
||||
| r | | by relative path |
|
||||
| s | | by size |
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
backspace | | remove last character
|
||||
ctrl-c | | terminate
|
||||
ctrl-u | | remove line
|
||||
ctrl-w | | remove last word
|
||||
enter | | rename
|
||||
esc | | cancel
|
||||
### number
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| down | j | to down |
|
||||
| enter | | to index |
|
||||
| f1 | | global help menu |
|
||||
| k | up | to up |
|
||||
| [0-9] | | input |
|
||||
|
||||
### copy_to
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| tab | | try complete |
|
||||
|
||||
### edit_permissions
|
||||
|
||||
| key | remaps | action |
|
||||
| ------ | ------ | ---------------- |
|
||||
| G | | -group |
|
||||
| M | | min |
|
||||
| O | | -other |
|
||||
| U | | -user |
|
||||
| ctrl-r | | reset |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| g | | +group |
|
||||
| m | | max |
|
||||
| o | | +other |
|
||||
| u | | +user |
|
||||
|
||||
### delete
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
D | | force delete
|
||||
ctrl-c | | terminate
|
||||
d | | delete
|
||||
esc | | cancel
|
||||
|
||||
|
||||
### sort
|
||||
| key | remaps | action |
|
||||
| --- | ------ | ---------------- |
|
||||
| D | | force delete |
|
||||
| d | | delete |
|
||||
| f1 | | global help menu |
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
! | | reverse sorters
|
||||
E | | by canonical extension reverse
|
||||
M | | by canonical mime essence reverse
|
||||
N | | by node type reverse
|
||||
R | | by relative path reverse
|
||||
S | | by size reverse
|
||||
backspace | | remove last sorter
|
||||
ctrl-c | | terminate
|
||||
ctrl-r | | reset sorters
|
||||
ctrl-u | | clear sorters
|
||||
e | | by canonical extension
|
||||
enter | esc | done
|
||||
m | | by canonical mime essence
|
||||
n | | by node type
|
||||
r | | by relative path
|
||||
s | | by size
|
||||
### move_to
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| tab | | try complete |
|
||||
|
||||
### filter
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
R | | relative does not contain
|
||||
backspace | | remove last filter
|
||||
ctrl-c | | terminate
|
||||
ctrl-r | | reset filters
|
||||
ctrl-u | | clear filters
|
||||
enter | esc | done
|
||||
r | | relative does contain
|
||||
|
||||
|
||||
### relative path does contain
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
backspace | | remove last character
|
||||
ctrl-c | | terminate
|
||||
ctrl-u | | remove line
|
||||
ctrl-w | | remove last word
|
||||
enter | | apply filter
|
||||
esc | | cancel
|
||||
|
||||
|
||||
### relative path does not contain
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
backspace | | remove last character
|
||||
ctrl-c | | terminate
|
||||
ctrl-u | | remove line
|
||||
ctrl-w | | remove last word
|
||||
enter | | apply filter
|
||||
esc | | cancel
|
||||
|
||||
|
||||
### switch layout
|
||||
|
||||
key | remaps | action
|
||||
--------------- | ------------------------- | ------
|
||||
1 | | default
|
||||
2 | | no help menu
|
||||
3 | | no selection panel
|
||||
4 | | no help or selection
|
||||
ctrl-c | | terminate
|
||||
esc | | cancel
|
||||
|
||||
|
||||
[1]:https://www.vim.org/
|
||||
[2]:https://github.com/jarun/nnn/
|
||||
[3]:#default
|
||||
[4]:modes.md
|
||||
| key | remaps | action |
|
||||
| --------- | ------ | ---------------------------------- |
|
||||
| R | | relative path does not match regex |
|
||||
| backspace | | remove last filter |
|
||||
| ctrl-r | | reset filters |
|
||||
| ctrl-u | | clear filters |
|
||||
| f1 | | global help menu |
|
||||
| r | | relative path does match regex |
|
||||
|
||||
### duplicate_as
|
||||
|
||||
| key | remaps | action |
|
||||
| ----- | ------ | ---------------- |
|
||||
| enter | | submit |
|
||||
| f1 | | global help menu |
|
||||
| tab | | try complete |
|
||||
|
@ -0,0 +1,230 @@
|
||||
# Environment Variables and Pipes
|
||||
|
||||
Alternative to `CallLua`, `CallLuaSilently` messages that call Lua functions,
|
||||
there are `Call0`, `CallSilently0`, `BashExec0`, `BashExecSilently0` messages
|
||||
that call shell commands.
|
||||
|
||||
### Example: Simple file opener using xdg-open and $XPLR_FOCUS_PATH
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key["X"] = {
|
||||
help = "open",
|
||||
messages = {
|
||||
{
|
||||
BashExecSilently0 = [===[
|
||||
xdg-open "${XPLR_FOCUS_PATH:?}"
|
||||
]===],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
However, unlike the Lua functions, these shell commands have to read the useful
|
||||
information and send messages via environment variables and temporary files
|
||||
called "pipe"s. These environment variables and files are only available when
|
||||
a command is being executed.
|
||||
|
||||
### Example: Using Environment Variables and Pipes
|
||||
|
||||
```lua
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key["space"] = {
|
||||
help = "ask name and greet",
|
||||
messages = {
|
||||
{
|
||||
BashExec0 = [===[
|
||||
echo "What's your name?"
|
||||
|
||||
read name
|
||||
greeting="Hello $name!"
|
||||
message="$greeting You are inside $PWD"
|
||||
|
||||
"$XPLR" -m 'LogSuccess: %q' "$message"
|
||||
]===]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Now, when you press "space" in default mode, you will be prompted for your
|
||||
-- name. Enter your name to receive a nice greeting and to know your location.
|
||||
```
|
||||
|
||||
Visit the [**fzf integration tutorial**][19] for another example.
|
||||
|
||||
To see the environment variables and pipes, invoke the shell by typing `:!` in default
|
||||
mode and run the following command:
|
||||
|
||||
```
|
||||
env | grep ^XPLR
|
||||
```
|
||||
|
||||
You will see something like:
|
||||
|
||||
```
|
||||
XPLR=xplr
|
||||
XPLR_FOCUS_INDEX=0
|
||||
XPLR_MODE=action to
|
||||
XPLR_PIPE_SELECTION_OUT=/run/user/1000/xplr/session/122278/pipe/selection_out
|
||||
XPLR_INPUT_BUFFER=
|
||||
XPLR_PIPE_GLOBAL_HELP_MENU_OUT=/run/user/1000/xplr/session/122278/pipe/global_help_menu_out
|
||||
XPLR_PID=122278
|
||||
XPLR_PIPE_MSG_IN=/run/user/1000/xplr/session/122278/pipe/msg_in
|
||||
XPLR_PIPE_LOGS_OUT=/run/user/1000/xplr/session/122278/pipe/logs_out
|
||||
XPLR_PIPE_RESULT_OUT=/run/user/1000/xplr/session/122278/pipe/result_out
|
||||
XPLR_PIPE_HISTORY_OUT=/run/user/1000/xplr/session/122278/pipe/history_out
|
||||
XPLR_FOCUS_PATH=/home/sayanarijit/Documents/GitHub/xplr/docs/en/book
|
||||
XPLR_SESSION_PATH=/run/user/1000/xplr/session/122278
|
||||
XPLR_APP_VERSION=0.14.3
|
||||
XPLR_PIPE_DIRECTORY_NODES_OUT=/run/user/1000/xplr/session/122278/pipe/directory_nodes_out
|
||||
```
|
||||
|
||||
The environment variables starting with `XPLR_PIPE_` are the temporary files
|
||||
called ["pipe"s][18].
|
||||
|
||||
The other variables are single-line variables containing simple information:
|
||||
|
||||
- [XPLR][38]
|
||||
- [XPLR_APP_VERSION][30]
|
||||
- [XPLR_FOCUS_INDEX][31]
|
||||
- [XPLR_FOCUS_PATH][32]
|
||||
- [XPLR_INPUT_BUFFER][33]
|
||||
- [XPLR_INITIAL_PWD][40]
|
||||
- [XPLR_MODE][34]
|
||||
- [XPLR_PID][35]
|
||||
- [XPLR_SESSION_PATH][36]
|
||||
- [XPLR_VROOT][39]
|
||||
|
||||
### Environment variables
|
||||
|
||||
#### XPLR
|
||||
|
||||
The binary path of xplr command.
|
||||
|
||||
#### XPLR_APP_VERSION
|
||||
|
||||
Self-explanatory.
|
||||
|
||||
#### XPLR_FOCUS_INDEX
|
||||
|
||||
Contains the index of the currently focused item, as seen in
|
||||
[column-renderer/index][10].
|
||||
|
||||
#### XPLR_FOCUS_PATH
|
||||
|
||||
Contains the full path of the currently focused node.
|
||||
|
||||
#### XPLR_INITIAL_PWD
|
||||
|
||||
The $PWD then xplr started.
|
||||
|
||||
#### XPLR_INPUT_BUFFER
|
||||
|
||||
The line currently in displaying in the xplr input buffer. For e.g. the search
|
||||
input while searching. See [Reading Input][37].
|
||||
|
||||
#### XPLR_MODE
|
||||
|
||||
Contains the mode xplr is currently in, see [modes][11].
|
||||
|
||||
#### XPLR_PID
|
||||
|
||||
Contains the process ID of the current xplr process.
|
||||
|
||||
#### XPLR_SESSION_PATH
|
||||
|
||||
Contains the current session path, like /tmp/runtime-"$USER"/xplr/session/"$XPLR_PID"/,
|
||||
you can find temporary files here, such as pipes.
|
||||
|
||||
#### XPLR_VROOT
|
||||
|
||||
Contains the path of current virtual root, is set.
|
||||
|
||||
### Pipes
|
||||
|
||||
#### Input pipe
|
||||
|
||||
Currently there is only one input pipe.
|
||||
|
||||
- [XPLR_PIPE_MSG_IN][20]
|
||||
|
||||
#### Output pipes
|
||||
|
||||
`XPLR_PIPE_*_OUT` are the output pipes that contain data which cannot be
|
||||
exposed directly via environment variables, like multi-line strings.
|
||||
These pipes can be accessed as plain text files located in $XPLR_SESSION_PATH.
|
||||
|
||||
Depending on the message (e.g. `Call` or `Call0`), each line will be separated
|
||||
by newline or null character (`\n` or `\0`).
|
||||
|
||||
- [XPLR_PIPE_SELECTION_OUT][21]
|
||||
- [XPLR_PIPE_GLOBAL_HELP_MENU_OUT][22]
|
||||
- [XPLR_PIPE_LOGS_OUT][23]
|
||||
- [XPLR_PIPE_RESULT_OUT][24]
|
||||
- [XPLR_PIPE_HISTORY_OUT][25]
|
||||
- [XPLR_PIPE_DIRECTORY_NODES_OUT][26]
|
||||
|
||||
#### XPLR_PIPE_MSG_IN
|
||||
|
||||
Append new messages to this pipe in [YAML][27] (or [JSON][7]) syntax. These
|
||||
messages will be read and handled by xplr after the command execution.
|
||||
|
||||
Depending on the message (e.g. `Call` or `Call0`) you need to separate each
|
||||
message using newline or null character (`\n` or `\0`).
|
||||
|
||||
> **_NOTE:_** Since version `v0.20.0`, it's recommended to avoid writing
|
||||
> directly to this file, as safely escaping YAML strings is a lot of work. Use
|
||||
> `xplr -m` / `xplr --pipe-msg-in` to pass messages to xplr in a safer way.
|
||||
>
|
||||
> It uses [jf][41] syntax to safely convert an YAML template into a valid message.
|
||||
>
|
||||
> Example: `"$XPLR" -m 'ChangeDirectory: %q' "${HOME:?}"`
|
||||
|
||||
#### XPLR_PIPE_SELECTION_OUT
|
||||
|
||||
List of selected paths.
|
||||
|
||||
#### XPLR_PIPE_GLOBAL_HELP_MENU_OUT
|
||||
|
||||
The full help menu.
|
||||
|
||||
#### XPLR_PIPE_LOGS_OUT
|
||||
|
||||
List of logs.
|
||||
|
||||
#### XPLR_PIPE_RESULT_OUT
|
||||
|
||||
Result (selected paths if any, else the focused path)
|
||||
|
||||
#### XPLR_PIPE_HISTORY_OUT
|
||||
|
||||
List of last visited paths (similar to jump list in vim).
|
||||
|
||||
#### XPLR_PIPE_DIRECTORY_NODES_OUT
|
||||
|
||||
List of paths, filtered and sorted as displayed in the [files table][28].
|
||||
|
||||
[7]: https://www.json.org
|
||||
[10]: column-renderer.md#index
|
||||
[11]: modes.md#modes
|
||||
[18]: #pipes
|
||||
[19]: configure-key-bindings.md#tutorial-adding-a-new-mode
|
||||
[20]: #xplr_pipe_msg_in
|
||||
[21]: #xplr_pipe_selection_out
|
||||
[22]: #xplr_pipe_global_help_menu_out
|
||||
[23]: #xplr_pipe_logs_out
|
||||
[24]: #xplr_pipe_result_out
|
||||
[25]: #xplr_pipe_history_out
|
||||
[26]: #xplr_pipe_directory_nodes_out
|
||||
[27]: https://www.yaml.org
|
||||
[28]: layout.md#table
|
||||
[30]: #xplr_app_version
|
||||
[31]: #xplr_focus_index
|
||||
[32]: #xplr_focus_path
|
||||
[33]: #xplr_input_buffer
|
||||
[34]: #xplr_mode
|
||||
[35]: #xplr_pid
|
||||
[36]: #xplr_session_path
|
||||
[37]: messages.md#reading-input
|
||||
[38]: #xplr
|
||||
[39]: #xplr_vroot
|
||||
[40]: #xplr_initial_pwd
|
||||
[41]: https://github.com/sayanarijit/jf
|
@ -1,188 +1,628 @@
|
||||
General Config
|
||||
==============
|
||||
### General Configuration
|
||||
|
||||
This configuration is exposed via the `xplr.config.general` API. It contains
|
||||
the following fields:
|
||||
The general configuration properties are grouped together in
|
||||
`xplr.config.general`.
|
||||
|
||||
#### xplr.config.general.disable_debug_error_mode
|
||||
|
||||
enable_mouse
|
||||
------------
|
||||
Set it to `true` if you want to ignore the startup errors. You can still see
|
||||
the errors in the logs.
|
||||
|
||||
Type: boolean
|
||||
|
||||
Set it to `true` enable scrolling using mouse.
|
||||
#### xplr.config.general.enable_mouse
|
||||
|
||||
|
||||
show_hidden
|
||||
-----------
|
||||
Set it to `true` if you want to enable mouse scrolling.
|
||||
|
||||
Type: boolean
|
||||
|
||||
Set it to `true` to show hidden files.
|
||||
|
||||
#### xplr.config.general.show_hidden
|
||||
|
||||
read_only
|
||||
---------
|
||||
Set it to `true` to show hidden files by default.
|
||||
|
||||
Type: boolean
|
||||
|
||||
#### xplr.config.general.read_only
|
||||
|
||||
Set it to `true` to use only a subset of selected operations that forbids
|
||||
executing commands or performing write operations on the file-system.
|
||||
|
||||
Type: boolean
|
||||
|
||||
#### xplr.config.general.enable_recover_mode
|
||||
|
||||
enable_recover_mode
|
||||
--------------------
|
||||
Set it to `true` if you want to enable a safety feature that will save you
|
||||
from yourself when you type recklessly.
|
||||
|
||||
Type: boolean
|
||||
|
||||
Set it to `true` if you want to enable a safety feature that will save you from
|
||||
yourself when you type recklessly.
|
||||
#### xplr.config.general.hide_remaps_in_help_menu
|
||||
|
||||
Set it to `true` if you want to hide all remaps in the help menu.
|
||||
|
||||
cursor.format
|
||||
-------------
|
||||
Type: boolean
|
||||
|
||||
#### xplr.config.general.paginated_scrolling
|
||||
|
||||
Set it to `true` if you want paginated scrolling.
|
||||
|
||||
Type: boolean
|
||||
|
||||
#### xplr.config.general.scroll_padding
|
||||
|
||||
Set the padding value to the scroll area.
|
||||
Only applicable when `xplr.config.general.paginated_scrolling = false`.
|
||||
|
||||
Type: boolean
|
||||
|
||||
#### xplr.config.general.enforce_bounded_index_navigation
|
||||
|
||||
Set it to `true` if you want the cursor to stay in the same position when
|
||||
the focus is on the first path and you navigate to the previous path
|
||||
(by pressing `up`/`k`), or when the focus is on the last path and you
|
||||
navigate to the next path (by pressing `down`/`j`).
|
||||
The default behavior is to rotate from the last/first path.
|
||||
|
||||
Type: boolean
|
||||
|
||||
#### xplr.config.general.prompt.format
|
||||
|
||||
This is the shape of the prompt for the input buffer.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
This is the shape of the cursor visible when the input buffer contains some string.
|
||||
#### xplr.config.general.prompt.style
|
||||
|
||||
This is the style of the prompt for the input buffer.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
cursor.style
|
||||
------------
|
||||
#### xplr.config.general.logs.info.format
|
||||
|
||||
Type: [Style][1]
|
||||
The string to indicate an information in logs.
|
||||
|
||||
Style of the cursor.
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.logs.info.style
|
||||
|
||||
initial_layout
|
||||
--------------
|
||||
The style for the information logs.
|
||||
|
||||
Type: string
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
The name of one of the [layout][2] to use when xplr loads.
|
||||
#### xplr.config.general.logs.success.format
|
||||
|
||||
The string to indicate an success in logs.
|
||||
|
||||
initial_mode
|
||||
------------
|
||||
Type: nullable string
|
||||
|
||||
Type: string
|
||||
#### xplr.config.general.logs.success.style
|
||||
|
||||
The name of one of the [mode][3] to use when xplr loads.
|
||||
The style for the success logs.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
initial_sorting
|
||||
---------------
|
||||
#### xplr.config.general.logs.warning.format
|
||||
|
||||
Type: list of [Node Sorter Applicable][4]
|
||||
The string to indicate an warnings in logs.
|
||||
|
||||
Initial group if sorters applied to the nodes list in the table.
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.logs.warning.style
|
||||
|
||||
The style for the warnings logs.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.logs.error.format
|
||||
|
||||
The string to indicate an error in logs.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.logs.error.style
|
||||
|
||||
The style for the error logs.
|
||||
|
||||
table.style
|
||||
-----------
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
Type: [Style][1]
|
||||
#### xplr.config.general.table.header.cols
|
||||
|
||||
Columns to display in the table header.
|
||||
|
||||
Type: nullable list of tables with the following fields:
|
||||
|
||||
- format: nullable string
|
||||
- style: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.table.header.style
|
||||
|
||||
Style of the table header.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.table.header.height
|
||||
|
||||
Height of the table header.
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
#### xplr.config.general.table.row.cols
|
||||
|
||||
Columns to display in each row in the table.
|
||||
|
||||
Type: nullable list of tables with the following fields:
|
||||
|
||||
- format: nullable string
|
||||
- style: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.table.row.style
|
||||
|
||||
Default style of the table.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.table.row.height
|
||||
|
||||
table.col_spacing
|
||||
-----------------
|
||||
Height of the table rows.
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
Default spacing of the columns in the table.
|
||||
#### xplr.config.general.table.style
|
||||
|
||||
Default style of the table.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.table.tree
|
||||
|
||||
Tree to display in the table.
|
||||
|
||||
table.col_widths
|
||||
----------------
|
||||
Type: nullable list of tables with the following fields:
|
||||
|
||||
Type: nullable list of [Constraint][5]
|
||||
- format: nullable string
|
||||
- style: [Style](https://xplr.dev/en/style)
|
||||
|
||||
Width of each column in the table.
|
||||
#### xplr.config.general.table.col_spacing
|
||||
|
||||
table.header.height
|
||||
----------------
|
||||
Spacing between the columns in the table.
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
Height of the table header.
|
||||
#### xplr.config.general.table.col_widths
|
||||
|
||||
Constraint for the column widths.
|
||||
|
||||
Type: nullable list of [Constraint](https://xplr.dev/en/layouts#constraint)
|
||||
|
||||
table.header.style
|
||||
---------------
|
||||
#### xplr.config.general.selection.item.format
|
||||
|
||||
Type: [Style][1]
|
||||
Renderer for each item in the selection list.
|
||||
|
||||
Style of table header.
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.selection.item.style
|
||||
|
||||
table.header.cols
|
||||
-----------------
|
||||
Style for each item in the selection list.
|
||||
|
||||
Type: List of column configuration
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
Each column config contains `format` field (string) and `style` field
|
||||
([Style][1]), that define the content and style of header.
|
||||
#### xplr.config.general.search.algorithm
|
||||
|
||||
The default search algorithm
|
||||
|
||||
table.row.height
|
||||
----------------
|
||||
Type: [Search Algorithm](https://xplr.dev/en/searching#algorithm)
|
||||
|
||||
Type: nullable integer
|
||||
#### xplr.config.general.search.unordered
|
||||
|
||||
The default search ordering
|
||||
|
||||
Type: boolean
|
||||
|
||||
#### xplr.config.general.default_ui.prefix
|
||||
|
||||
The content that is placed before the item name for each row by default.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.default_ui.suffix
|
||||
|
||||
The content which is appended to each item name for each row by default.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.default_ui.style
|
||||
|
||||
The default style of each item for each row.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.focus_ui.prefix
|
||||
|
||||
The string placed before the item name for a focused row.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.focus_ui.suffix
|
||||
|
||||
The string placed after the item name for a focused row.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.focus_ui.style
|
||||
|
||||
Style for focused item.
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.selection_ui.prefix
|
||||
|
||||
The string placed before the item name for a selected row.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.selection_ui.suffix
|
||||
|
||||
The string placed after the item name for a selected row.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.selection_ui.style
|
||||
|
||||
Style for selected rows.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.focus_selection_ui.prefix
|
||||
|
||||
The string placed before item name for a selected row that gets the focus.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.focus_selection_ui.suffix
|
||||
|
||||
The string placed after the item name for a selected row that gets the focus.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.focus_selection_ui.style
|
||||
|
||||
Style for a selected row that gets the focus.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.separator.format
|
||||
|
||||
The shape of the separator for the Sort & filter panel.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.separator.style
|
||||
|
||||
The style of the separator for the Sort & filter panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.default_identifier.format
|
||||
|
||||
The content of the default identifier in Sort & filter panel.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.default_identifier.style
|
||||
|
||||
Style for the default identifier in Sort & filter panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.forward.format
|
||||
|
||||
The shape of the forward direction indicator for sort identifiers in Sort & filter panel.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.forward.style
|
||||
|
||||
Style of forward direction indicator in Sort & filter panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.reverse.format
|
||||
|
||||
The shape of the reverse direction indicator for sort identifiers in Sort & filter panel.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.reverse.style
|
||||
|
||||
Style of reverse direction indicator in Sort & filter panel.
|
||||
|
||||
Height of each row in the table.
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.sorter_identifiers
|
||||
|
||||
table.row.style
|
||||
---------------
|
||||
The identifiers used to denote applied sorters in the Sort & filter panel.
|
||||
|
||||
Type: [Style][1]
|
||||
Type: nullable mapping of the following key-value pairs:
|
||||
|
||||
Style of table rows.
|
||||
- key: [Sorter](https://xplr.dev/en/sorting#sorter)
|
||||
- value:
|
||||
- format: nullable string
|
||||
- style: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.filter_identifiers
|
||||
|
||||
table.row.cols
|
||||
-----------------
|
||||
The identifiers used to denote applied filters in the Sort & filter panel.
|
||||
|
||||
Type: List of column configuration
|
||||
Type: nullable mapping of the following key-value pairs:
|
||||
|
||||
Each column config contains `format` field (string) and `style` field
|
||||
([Style][1]).
|
||||
- key: [Filter](https://xplr.dev/en/filtering#filter)
|
||||
- value:
|
||||
- format: nullable string
|
||||
- style: [Style](https://xplr.dev/en/style)
|
||||
|
||||
However, unlike [table.header.cols][6], the `format` field here
|
||||
points to a [column renderer function][7].
|
||||
#### xplr.config.general.sort_and_filter_ui.search_identifiers
|
||||
|
||||
The identifiers used to denote applied search input.
|
||||
|
||||
table.tree
|
||||
-----------------
|
||||
Type: { format = nullable string, style = [Style](https://xplr.dev/en/style) }
|
||||
|
||||
Type: List of tree configuration
|
||||
#### xplr.config.general.sort_and_filter_ui.search_direction_identifiers.ordered.format
|
||||
|
||||
It expects a list of three items. The first component of the tree, then the
|
||||
middle components, and finally the last component of the tree.
|
||||
The shape of ordered indicator for search ordering identifiers in Sort & filter panel.
|
||||
|
||||
Each item requires the `format` field which is a string, and the `style` field,
|
||||
which is the [Style][1] object.
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.sort_and_filter_ui.search_direction_identifiers.unordered.format
|
||||
|
||||
The shape of unordered indicator for search ordering identifiers in Sort & filter panel.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.panel_ui.default.title.format
|
||||
|
||||
The content for panel title by default.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.panel_ui.default.title.style
|
||||
|
||||
The style for panel title by default.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.default.style
|
||||
|
||||
Style of the panels by default.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.default.borders
|
||||
|
||||
Defines where to show borders for the panels by default.
|
||||
|
||||
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
|
||||
#### xplr.config.general.panel_ui.default.border_type
|
||||
|
||||
Type of the borders by default.
|
||||
|
||||
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
|
||||
#### xplr.config.general.panel_ui.default.border_style
|
||||
|
||||
Style of the panel borders by default.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.table.title.format
|
||||
|
||||
The content for the table panel title.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.panel_ui.table.title.style
|
||||
|
||||
Style of the table panel title.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.table.style
|
||||
|
||||
Style of the table panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.table.borders
|
||||
|
||||
Defines where to show borders for the table panel.
|
||||
|
||||
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
|
||||
#### xplr.config.general.panel_ui.table.border_type
|
||||
|
||||
Type of the borders for table panel.
|
||||
|
||||
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
|
||||
#### xplr.config.general.panel_ui.table.border_style
|
||||
|
||||
Style of the table panel borders.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.help_menu.title.format
|
||||
|
||||
The content for the help menu panel title.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.panel_ui.help_menu.title.style
|
||||
|
||||
Style of the help menu panel title.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.help_menu.style
|
||||
|
||||
Style of the help menu panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.help_menu.borders
|
||||
|
||||
Defines where to show borders for the help menu panel.
|
||||
|
||||
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
|
||||
#### xplr.config.general.panel_ui.help_menu.border_type
|
||||
|
||||
Type of the borders for help menu panel.
|
||||
|
||||
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
|
||||
#### xplr.config.general.panel_ui.help_menu.border_style
|
||||
|
||||
Style of the help menu panel borders.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.input_and_logs.title.format
|
||||
|
||||
The content for the input & logs panel title.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
Example:
|
||||
#### xplr.config.general.panel_ui.input_and_logs.title.style
|
||||
|
||||
```lua
|
||||
xplr.config.general.table.tree = {
|
||||
{ format = "├─", style = { add_modifiers = { "Bold" }, bg = nil, fg = "Blue", sub_modifiers = nil } },
|
||||
{ format = "├─", style = { add_modifiers = { "Bold" }, bg = nil, fg = "Blue", sub_modifiers = nil } },
|
||||
{ format = "╰─", style = { add_modifiers = { "Bold" }, bg = nil, fg = "Blue", sub_modifiers = nil } },
|
||||
}
|
||||
```
|
||||
Style of the input & logs panel title.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.input_and_logs.borders
|
||||
|
||||
#### xplr.config.general.panel_ui.input_and_logs.style
|
||||
|
||||
Style of the input & logs panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
Defines where to show borders for the input & logs panel.
|
||||
|
||||
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
|
||||
#### xplr.config.general.panel_ui.input_and_logs.border_type
|
||||
|
||||
Type of the borders for input & logs panel.
|
||||
|
||||
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
|
||||
#### xplr.config.general.panel_ui.input_and_logs.border_style
|
||||
|
||||
Style of the input & logs panel borders.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.selection.title.format
|
||||
|
||||
The content for the selection panel title.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.panel_ui.selection.title.style
|
||||
|
||||
Style of the selection panel title.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.selection.borders
|
||||
|
||||
#### xplr.config.general.panel_ui.selection.style
|
||||
|
||||
Style of the selection panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
Defines where to show borders for the selection panel.
|
||||
|
||||
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
|
||||
#### xplr.config.general.panel_ui.selection.border_type
|
||||
|
||||
Type of the borders for selection panel.
|
||||
|
||||
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
|
||||
#### xplr.config.general.panel_ui.selection.border_style
|
||||
|
||||
Style of the selection panel borders.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.sort_and_filter.title.format
|
||||
|
||||
The content for the sort & filter panel title.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.panel_ui.sort_and_filter.title.style
|
||||
|
||||
Style of the sort & filter panel title.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.sort_and_filter.style
|
||||
|
||||
Style of the sort & filter panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.sort_and_filter.borders
|
||||
|
||||
Defines where to show borders for the sort & filter panel.
|
||||
|
||||
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
|
||||
#### xplr.config.general.panel_ui.sort_and_filter.border_type
|
||||
|
||||
Type of the borders for sort & filter panel.
|
||||
|
||||
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
|
||||
#### xplr.config.general.panel_ui.sort_and_filter.border_style
|
||||
|
||||
Style of the sort & filter panel borders.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.initial_sorting
|
||||
|
||||
Initial group if sorters applied to the nodes list in the table.
|
||||
|
||||
Type: nullable list of [Node Sorter](https://xplr.dev/en/sorting#node-sorter-applicable)
|
||||
|
||||
#### xplr.config.general.initial_mode
|
||||
|
||||
The name of one of the modes to use when xplr loads.
|
||||
This isn't the default mode. To modify the default mode, overwrite
|
||||
[xplr.config.modes.builtin.default](https://xplr.dev/en/modes#xplrconfigmodesbuiltindefault).
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.initial_layout
|
||||
|
||||
The name of one of the layouts to use when xplr loads.
|
||||
This isn't the default layout. To modify the default layout, overwrite
|
||||
[xplr.config.layouts.builtin.default](https://xplr.dev/en/layouts#xplrconfiglayoutsbuiltindefault).
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.start_fifo
|
||||
|
||||
Set it to a file path to start fifo when xplr loads.
|
||||
Generally it is used to integrate with external tools like previewers.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
TODO: Continue documentation.
|
||||
#### xplr.config.general.global_key_bindings
|
||||
|
||||
Use it to define a set of key bindings that are available by default in
|
||||
every [mode](https://xplr.dev/en/mode). They can be overwritten.
|
||||
|
||||
[1]:style.md
|
||||
[2]:layouts.md
|
||||
[3]:modes.md
|
||||
[4]:sorting.md#node-sorter-applicable
|
||||
[5]:layouts.md#constraint
|
||||
[6]:#tableheadercols
|
||||
[7]:column-renderer.md
|
||||
Type: [Key Bindings](https://xplr.dev/en/configure-key-bindings#key-bindings)
|
||||
|
@ -0,0 +1,27 @@
|
||||
# Input Operation
|
||||
|
||||
Cursor based input operation is a [sum type][3] can be one of the following:
|
||||
|
||||
- { SetCursor = int }
|
||||
- { InsertCharacter = str }
|
||||
- "GoToPreviousCharacter"
|
||||
- "GoToNextCharacter"
|
||||
- "GoToPreviousWord"
|
||||
- "GoToNextWord"
|
||||
- "GoToStart"
|
||||
- "GoToEnd"
|
||||
- "DeletePreviousCharacter"
|
||||
- "DeleteNextCharacter"
|
||||
- "DeletePreviousWord"
|
||||
- "DeleteNextWord"
|
||||
- "DeleteLine"
|
||||
- "DeleteTillEnd"
|
||||
|
||||
## Also See:
|
||||
|
||||
- [Message][1]
|
||||
- [Full List of Messages][2]
|
||||
|
||||
[1]: message.md
|
||||
[2]: messages.md
|
||||
[3]: sum-type.md
|
@ -1,10 +1,8 @@
|
||||
Integration
|
||||
===========
|
||||
# Integration
|
||||
|
||||
xplr is designed to integrate well with other tools and commands. It can be
|
||||
used as a file picker or a pluggable file manager.
|
||||
|
||||
- [Awesome Integrations][1]
|
||||
|
||||
|
||||
[1]:awesome-integrations.md
|
||||
[1]: awesome-integrations.md
|
||||
|
@ -1 +1,16 @@
|
||||
# Key Bindings
|
||||
# Key Bindings
|
||||
|
||||
Key bindings define how each keyboard input will be handled while in a specific
|
||||
[mode][4].
|
||||
|
||||
See the [Default key bindings][1] for example.
|
||||
|
||||
To configure or work with key bindings, visit [Configure Key Bindings][2].
|
||||
|
||||
In case you need help debugging key bindings or to understand the system DYI
|
||||
way, refer to the [Debug Key Bindings][3] guide.
|
||||
|
||||
[1]: default-key-bindings.md
|
||||
[2]: configure-key-bindings.md
|
||||
[3]: debug-key-bindings.md
|
||||
[4]: modes.md#mode
|
||||
|
@ -0,0 +1,597 @@
|
||||
# Layout
|
||||
|
||||
#### Example: Defining Custom Layout
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = {
|
||||
Horizontal = {
|
||||
config = {
|
||||
margin = 1,
|
||||
horizontal_margin = 1,
|
||||
vertical_margin = 1,
|
||||
constraints = {
|
||||
{ Percentage = 50 },
|
||||
{ Percentage = 50 },
|
||||
}
|
||||
},
|
||||
splits = {
|
||||
"Table",
|
||||
"HelpMenu",
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭ /home ─────────────╮╭ Help [default] ────╮
|
||||
│ ╭─── path ││. show hidden │
|
||||
│ ├▸[ð Desktop/] ││/ search │
|
||||
│ ├ ð Documents/ ││: action │
|
||||
│ ├ ð Downloads/ ││? global help │
|
||||
│ ├ ð GitHub/ ││G go to bottom │
|
||||
│ ├ ð Music/ ││V select/unselect│
|
||||
│ ├ ð Pictures/ ││ctrl duplicate as │
|
||||
│ ├ ð Public/ ││ctrl next visit │
|
||||
╰────────────────────╯╰────────────────────╯
|
||||
```
|
||||
|
||||
A layout is a [sum type][56] can be one of the following:
|
||||
|
||||
- [Nothing][8]
|
||||
- [Table][9]
|
||||
- [InputAndLogs][10]
|
||||
- [Selection][11]
|
||||
- [HelpMenu][12]
|
||||
- [SortAndFilter][13]
|
||||
- [Static][25]
|
||||
- [Dynamic][26]
|
||||
- [Horizontal][14]
|
||||
- [Vertical][16]
|
||||
- CustomContent (deprecated, use `Static` or `Dynamic`)
|
||||
|
||||
### Nothing
|
||||
|
||||
This layout contains a blank panel.
|
||||
|
||||
Type: "Nothing"
|
||||
|
||||
### Table
|
||||
|
||||
This layout contains the table displaying the files and directories in the current
|
||||
directory.
|
||||
|
||||
### InputAndLogs
|
||||
|
||||
This layout contains the panel displaying the input prompt and logs.
|
||||
|
||||
Type: "InputAndLogs"
|
||||
|
||||
### Selection
|
||||
|
||||
This layout contains the panel displaying the selected paths.
|
||||
|
||||
Type: "Selection"
|
||||
|
||||
### HelpMenu
|
||||
|
||||
This layout contains the panel displaying the help menu for the current mode in
|
||||
real-time.
|
||||
|
||||
Type: "HelpMenu"
|
||||
|
||||
### SortAndFilter
|
||||
|
||||
This layout contains the panel displaying the pipeline of sorters and filters applied on
|
||||
the list of paths being displayed.
|
||||
|
||||
Type: "SortAndFilter"
|
||||
|
||||
### Static
|
||||
|
||||
This is a custom layout to render static content.
|
||||
|
||||
Type: { Static = [Custom Panel][27] }
|
||||
|
||||
### Dynamic
|
||||
|
||||
This is a custom layout to render dynamic content using a function defined in
|
||||
[xplr.fn][28] that takes [Content Renderer Argument][36] and returns [Custom Panel][27].
|
||||
|
||||
Type: { Dynamic = "[Content Renderer][35]" }
|
||||
|
||||
### Horizontal
|
||||
|
||||
This is a special layout that splits the panel into two horizontal parts.
|
||||
|
||||
It contains the following information:
|
||||
|
||||
- [config][15]
|
||||
- [splits][17]
|
||||
|
||||
Type: { Vertical = { config = [Layout Config][15], splits = { [Layout][17], ... } }
|
||||
|
||||
### Vertical
|
||||
|
||||
This is a special layout that splits the panel into two vertical parts.
|
||||
|
||||
It contains the following information:
|
||||
|
||||
- [config][15]
|
||||
- [splits][17]
|
||||
|
||||
Type: { Vertical = { config = [Layout Config][15], splits = { [Layout][17], ... } }
|
||||
|
||||
## Layout Config
|
||||
|
||||
A layout config contains the following information:
|
||||
|
||||
- [margin][18]
|
||||
- [horizontal_margin][19]
|
||||
- [vertical_margin][20]
|
||||
- [constraints][21]
|
||||
|
||||
### margin
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
The width of the margin in all direction.
|
||||
|
||||
### horizontal_Margin
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
The width of the horizontal margins. Overwrites the [margin][18] value.
|
||||
|
||||
### vertical_Margin
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
The width of the vertical margins. Overwrites the [margin][18] value.
|
||||
|
||||
### constraints
|
||||
|
||||
Type: nullable list of [Constraint][22]
|
||||
|
||||
The constraints applied on the layout.
|
||||
|
||||
## Constraint
|
||||
|
||||
A constraint is a [sum type][56] can be one of the following:
|
||||
|
||||
- { Percentage = int }
|
||||
- { Ratio = { int, int } }
|
||||
- { Length = { int }
|
||||
- { LengthLessThanScreenHeight = int }
|
||||
- { LengthLessThanScreenWidth = int }
|
||||
- { LengthLessThanLayoutHeight = int }
|
||||
- { LengthLessThanLayoutWidth = int }
|
||||
- { Max = int }
|
||||
- { MaxLessThanScreenHeight = int }
|
||||
- { MaxLessThanScreenWidth = int }
|
||||
- { MaxLessThanLayoutHeight = int }
|
||||
- { MaxLessThanLayoutWidth = int }
|
||||
- { Min = int }
|
||||
- { MinLessThanScreenHeight = int }
|
||||
- { MinLessThanScreenWidth = int }
|
||||
- { MinLessThanLayoutHeight = int }
|
||||
- { MinLessThanLayoutWidth = int }
|
||||
|
||||
## splits
|
||||
|
||||
Type: list of [Layout][3]
|
||||
|
||||
The list of child layouts to fit into the parent layout.
|
||||
|
||||
## Custom Panel
|
||||
|
||||
Custom panel is a [sum type][56] can be one of the following:
|
||||
|
||||
- [CustomParagraph][29]
|
||||
- [CustomList][30]
|
||||
- [CustomTable][31]
|
||||
- [CustomLayout][55]
|
||||
|
||||
### CustomParagraph
|
||||
|
||||
A paragraph to render. It contains the following fields:
|
||||
|
||||
- **ui** (nullable [Panel UI Config][32]): Optional UI config for the panel.
|
||||
- **body** (string): The string to render.
|
||||
|
||||
#### Example: Render a custom static paragraph
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = {
|
||||
Static = {
|
||||
CustomParagraph = {
|
||||
ui = { title = { format = " custom title " } },
|
||||
body = "custom body",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭ custom title ────────╮
|
||||
│custom body │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────╯
|
||||
```
|
||||
|
||||
#### Example: Render a custom dynamic paragraph
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }
|
||||
|
||||
xplr.fn.custom.render_layout = function(ctx)
|
||||
return {
|
||||
CustomParagraph = {
|
||||
ui = { title = { format = ctx.app.pwd } },
|
||||
body = xplr.util.to_yaml(ctx.app.focused_node),
|
||||
},
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭/home/sayanarijit───────────────────────────╮
|
||||
│mime_essence: inode/directory │
|
||||
│relative_path: Desktop │
|
||||
│is_symlink: false │
|
||||
│is_readonly: false │
|
||||
│parent: /home/sayanarijit │
|
||||
│absolute_path: /home/sayanarijit/Desktop │
|
||||
│is_broken: false │
|
||||
│created: 1668087850396758714 │
|
||||
│size: 4096 │
|
||||
│gid: 100 │
|
||||
╰────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
### CustomList
|
||||
|
||||
A list to render. It contains the following fields:
|
||||
|
||||
- **ui** (nullable [Panel UI Config][32]): Optional UI config for the panel.
|
||||
- **body** (list of string): The list of strings to display.
|
||||
|
||||
#### Example: Render a custom static list
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = {
|
||||
Static = {
|
||||
CustomList = {
|
||||
ui = { title = { format = " custom title " } },
|
||||
body = { "1", "2", "3" },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭ custom title ─────────────╮
|
||||
│1 │
|
||||
│2 │
|
||||
│3 │
|
||||
│ │
|
||||
╰───────────────────────────╯
|
||||
```
|
||||
|
||||
#### Example: Render a custom dynamic list
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }
|
||||
|
||||
xplr.fn.custom.render_layout = function(ctx)
|
||||
return {
|
||||
CustomList = {
|
||||
ui = { title = { format = ctx.app.pwd } },
|
||||
body = {
|
||||
(ctx.app.focused_node or {}).relative_path or "",
|
||||
ctx.app.version,
|
||||
tostring(ctx.app.pid),
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭/home/sayanarijit──────────╮
|
||||
│Desktop │
|
||||
│0.21.2 │
|
||||
│17336 │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────╯
|
||||
```
|
||||
|
||||
## CustomTable
|
||||
|
||||
A custom table to render. It contains the following fields:
|
||||
|
||||
- **ui** (nullable [Panel UI Config][32]): Optional UI config for the panel.
|
||||
- **widths** (list of [Constraint][22]): Width of the columns.
|
||||
- **col_spacing** (nullable int): Spacing between columns. Defaults to 1.
|
||||
- **body** (list of list of string): The rows and columns to render.
|
||||
|
||||
#### Example: Render a custom static table
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = {
|
||||
Static = {
|
||||
CustomTable = {
|
||||
ui = { title = { format = " custom title " } },
|
||||
widths = {
|
||||
{ Percentage = 50 },
|
||||
{ Percentage = 50 },
|
||||
},
|
||||
body = {
|
||||
{ "a", "b" },
|
||||
{ "c", "d" },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭ custom title ────────────────────╮
|
||||
│a b │
|
||||
│c d │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────╯
|
||||
```
|
||||
|
||||
#### Example: Render a custom dynamic table
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = {Dynamic = "custom.render_layout" }
|
||||
|
||||
xplr.fn.custom.render_layout = function(ctx)
|
||||
return {
|
||||
CustomTable = {
|
||||
ui = { title = { format = ctx.app.pwd } },
|
||||
widths = {
|
||||
{ Percentage = 50 },
|
||||
{ Percentage = 50 },
|
||||
},
|
||||
body = {
|
||||
{ "", "" },
|
||||
{ "Layout height", tostring(ctx.layout_size.height) },
|
||||
{ "Layout width", tostring(ctx.layout_size.width) },
|
||||
{ "", "" },
|
||||
{ "Screen height", tostring(ctx.screen_size.height) },
|
||||
{ "Screen width", tostring(ctx.screen_size.width) },
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭/home/sayanarijit───────────────────────────╮
|
||||
│ │
|
||||
│Layout height 12 │
|
||||
│Layout width 46 │
|
||||
│ │
|
||||
│Screen height 12 │
|
||||
│Screen width 46 │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
### CustomLayout
|
||||
|
||||
A whole custom layout to render. It doesn't make sense to use it as a
|
||||
[Static][25] layout, but can be very useful to render as a [Dynamic][26] layout
|
||||
for use cases where the structure of the layout needs to change without having
|
||||
to switch modes.
|
||||
|
||||
> WARNING: Rendering the same dynamic custom layout recursively will result in
|
||||
> a ugly crash.
|
||||
|
||||
#### Example: Render a custom dynamic layout
|
||||
|
||||
```lua
|
||||
xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }
|
||||
|
||||
xplr.fn.custom.render_layout = function(ctx)
|
||||
local inner = {
|
||||
config = {
|
||||
constraints = {
|
||||
{ Percentage = 50 },
|
||||
{ Percentage = 50 },
|
||||
},
|
||||
},
|
||||
splits = {
|
||||
{ Static = { CustomParagraph = { body = "Try your luck..." } } },
|
||||
{ Static = { CustomParagraph = { body = "Press ctrl-r" } } },
|
||||
},
|
||||
}
|
||||
|
||||
local layout_type = "Vertical"
|
||||
if math.random(1, 2) == 1 then
|
||||
layout_type = "Horizontal"
|
||||
end
|
||||
|
||||
return { CustomLayout = { [layout_type] = inner } }
|
||||
end
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
```
|
||||
╭─────────────────────╮╭─────────────────────╮
|
||||
│Try your luck... ││Press ctrl-r │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰─────────────────────╯╰─────────────────────╯
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```
|
||||
╭────────────────────────────────────────────╮
|
||||
│Try your luck... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰────────────────────────────────────────────╯
|
||||
╭────────────────────────────────────────────╮
|
||||
│Press ctrl-r │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
## Panel UI Config
|
||||
|
||||
It contains the following optional fields:
|
||||
|
||||
- **title** ({ format = "string", style = [Style][33] }): the title of the panel.
|
||||
- **style** ([Style][33]): The style of the panel body.
|
||||
- **borders** (nullable list of [Border][34]): The shape of the borders.
|
||||
- **border_type** ([Border Type][54]): The type of the borders.
|
||||
- **border_style** ([Style][33]): The style of the borders.
|
||||
|
||||
## Content Renderer
|
||||
|
||||
It is a Lua function that receives [a special argument][36] as input and
|
||||
returns some output that can be rendered in the UI. It is used to render
|
||||
content body for the custom dynamic layouts.
|
||||
|
||||
## Content Renderer Argument
|
||||
|
||||
It contains the following information:
|
||||
|
||||
- [layout_size][37]
|
||||
- [screen_size][37]
|
||||
- [scrolltop][57]
|
||||
- [app][38]
|
||||
|
||||
### Size
|
||||
|
||||
It contains the following information:
|
||||
|
||||
- x
|
||||
- y
|
||||
- height
|
||||
- width
|
||||
|
||||
Every field is of integer type.
|
||||
|
||||
### scrolltop
|
||||
|
||||
Type: integer
|
||||
|
||||
The start index of the visible nodes in the table.
|
||||
|
||||
### app
|
||||
|
||||
This is a lightweight version of the [Lua Context][39]. In this context, the
|
||||
heavyweight fields like [directory_buffer][50] are omitted for performance
|
||||
reasons.
|
||||
|
||||
Hence, only the following fields are available.
|
||||
|
||||
- [version][40]
|
||||
- [pwd][41]
|
||||
- [initial_pwd][53]
|
||||
- [vroot][52]
|
||||
- [focused_node][42]
|
||||
- [selection][43]
|
||||
- [mode][44]
|
||||
- [layout][45]
|
||||
- [input_buffer][46]
|
||||
- [pid][47]
|
||||
- [session_path][48]
|
||||
- [explorer_config][49]
|
||||
|
||||
## Also See:
|
||||
|
||||
- [xplr.config.layouts][51]
|
||||
|
||||
[1]: #builtin
|
||||
[2]: #custom
|
||||
[3]: #layout
|
||||
[4]: #default
|
||||
[5]: #no_help
|
||||
[6]: #no_selection
|
||||
[7]: #no_help_no_selection
|
||||
[8]: #nothing
|
||||
[9]: #table
|
||||
[10]: #inputandlogs
|
||||
[11]: #selection
|
||||
[12]: #helpmenu
|
||||
[13]: #sortandfilter
|
||||
[14]: #horizontal
|
||||
[15]: #layout-config
|
||||
[16]: #vertical
|
||||
[17]: #splits
|
||||
[18]: #margin
|
||||
[19]: #horizontal_margin
|
||||
[20]: #vertical_margin
|
||||
[21]: #constraints
|
||||
[22]: #constraint
|
||||
[25]: #static
|
||||
[26]: #dynamic
|
||||
[27]: #custom-panel
|
||||
[28]: configuration.md#function
|
||||
[29]: #customparagraph
|
||||
[30]: #customlist
|
||||
[31]: #customtable
|
||||
[32]: #panel-ui-config
|
||||
[33]: style.md#style
|
||||
[34]: borders.md#border
|
||||
[35]: #content-renderer
|
||||
[36]: #content-renderer-argument
|
||||
[37]: #size
|
||||
[38]: #app
|
||||
[39]: lua-function-calls.md#lua-context
|
||||
[40]: lua-function-calls.md#version
|
||||
[41]: lua-function-calls.md#pwd
|
||||
[42]: lua-function-calls.md#focused_node
|
||||
[43]: lua-function-calls.md#selection
|
||||
[44]: lua-function-calls.md#mode
|
||||
[45]: lua-function-calls.md#layout
|
||||
[46]: lua-function-calls.md#input_buffer
|
||||
[47]: lua-function-calls.md#pid
|
||||
[48]: lua-function-calls.md#session_path
|
||||
[49]: lua-function-calls.md#explorer_config
|
||||
[50]: lua-function-calls.md#directory_buffer
|
||||
[51]: layouts.md
|
||||
[52]: lua-function-calls.md#vroot
|
||||
[53]: lua-function-calls.md#initial_pwd
|
||||
[54]: borders.md#border-type
|
||||
[55]: #customlayout
|
||||
[56]: sum-type.md
|
||||
[57]: #scrolltop
|
@ -0,0 +1,444 @@
|
||||
# Lua Function Calls
|
||||
|
||||
xplr allows you to define lua functions using the `xplr.fn.custom` Lua API.
|
||||
|
||||
These functions can be called using messages like `CallLua`, `CallLuaSilently`.
|
||||
|
||||
When called the function receives a [special argument][14] that
|
||||
contains some useful information. The function can optionally return a list of
|
||||
messages which will be handled by xplr.
|
||||
|
||||
## Example: Using Lua Function Calls
|
||||
|
||||
```lua
|
||||
-- Define the function
|
||||
xplr.fn.custom.ask_name_and_greet = function(app)
|
||||
print("What's your name?")
|
||||
|
||||
local name = io.read()
|
||||
local greeting = "Hello " .. name .. "!"
|
||||
local message = greeting .. " You are inside " .. app.pwd
|
||||
|
||||
return {
|
||||
{ LogSuccess = message },
|
||||
}
|
||||
end
|
||||
|
||||
-- Map the function to a key (space)
|
||||
xplr.config.modes.builtin.default.key_bindings.on_key.space = {
|
||||
help = "ask name and greet",
|
||||
messages = {
|
||||
{ CallLua = "custom.ask_name_and_greet" }
|
||||
}
|
||||
}
|
||||
|
||||
-- Now, when you press "space" in default mode, you will be prompted for your
|
||||
-- name. Enter your name to receive a nice greeting and to know your location.
|
||||
```
|
||||
|
||||
Visit the [xplr.util][85] API docs for some useful utility / helper functions
|
||||
that you can use in your Lua function calls.
|
||||
|
||||
## Lua Context
|
||||
|
||||
This is a special argument passed to the lua functions when called using the
|
||||
`CallLua`, `CallLuaSilently` messages.
|
||||
|
||||
It contains the following information:
|
||||
|
||||
- [version][29]
|
||||
- [pwd][31]
|
||||
- [initial_pwd][76]
|
||||
- [vroot][75]
|
||||
- [focused_node][32]
|
||||
- [directory_buffer][33]
|
||||
- [selection][34]
|
||||
- [mode][35]
|
||||
- [layout][36]
|
||||
- [input_buffer][37]
|
||||
- [pid][38]
|
||||
- [session_path][39]
|
||||
- [explorer_config][40]
|
||||
- [history][41]
|
||||
- [last_modes][42]
|
||||
|
||||
### version
|
||||
|
||||
Type: string
|
||||
|
||||
xplr version. Can be used to test compatibility.
|
||||
|
||||
### pwd
|
||||
|
||||
Type: string
|
||||
|
||||
The present working directory.
|
||||
|
||||
### initial_pwd
|
||||
|
||||
Type: string
|
||||
|
||||
The initial working directory when xplr started.
|
||||
|
||||
### vroot
|
||||
|
||||
Type: nullable string
|
||||
|
||||
The current virtual root.
|
||||
|
||||
### focused_node
|
||||
|
||||
Type: nullable [Node][44]
|
||||
|
||||
The node under focus.
|
||||
|
||||
### directory_buffer
|
||||
|
||||
Type: nullable [Directory Buffer][62]
|
||||
|
||||
The directory buffer being rendered.
|
||||
|
||||
### selection
|
||||
|
||||
Type: list of selected [Node][44]s
|
||||
|
||||
The selected nodes.
|
||||
|
||||
### mode
|
||||
|
||||
Type: [Mode][8]
|
||||
|
||||
Current mode.
|
||||
|
||||
### layout
|
||||
|
||||
Type: [Layout][11]
|
||||
|
||||
Current layout.
|
||||
|
||||
### input_buffer
|
||||
|
||||
Type: nullable string
|
||||
|
||||
The input buffer.
|
||||
|
||||
### pid
|
||||
|
||||
Type: integer
|
||||
|
||||
The xplr session PID.
|
||||
|
||||
### session_path
|
||||
|
||||
Type: string
|
||||
|
||||
The session path.
|
||||
|
||||
### explorer_config
|
||||
|
||||
Type: [Explorer Config][66]
|
||||
|
||||
The configuration for exploring paths.
|
||||
|
||||
### history
|
||||
|
||||
Type: [History][70]
|
||||
|
||||
### last_modes
|
||||
|
||||
Type: list of [Mode][8]
|
||||
|
||||
Last modes, not popped yet.
|
||||
|
||||
## Node
|
||||
|
||||
A node contains the following fields:
|
||||
|
||||
- [parent][45]
|
||||
- [relative_path][46]
|
||||
- [absolute_path][47]
|
||||
- [extension][48]
|
||||
- [is_symlink][49]
|
||||
- [is_broken][50]
|
||||
- [is_dir][51]
|
||||
- [is_file][52]
|
||||
- [is_readonly][53]
|
||||
- [mime_essence][54]
|
||||
- [size][55]
|
||||
- [human_size][56]
|
||||
- [permissions][57]
|
||||
- [created][71]
|
||||
- [last_modified][72]
|
||||
- [uid][73]
|
||||
- [gid][74]
|
||||
- [canonical][58]
|
||||
- [symlink][59]
|
||||
|
||||
### parent
|
||||
|
||||
Type: string
|
||||
|
||||
The parent path of the node.
|
||||
|
||||
### relative_path
|
||||
|
||||
Type: string
|
||||
|
||||
The path relative to the parent, i.e. the file/directory name with extension.
|
||||
|
||||
### absolute_path
|
||||
|
||||
Type: string
|
||||
|
||||
The absolute path (without resolving symlinks) of the node.
|
||||
|
||||
### extension
|
||||
|
||||
Type: string
|
||||
|
||||
The extension of the node.
|
||||
|
||||
### is_symlink
|
||||
|
||||
Type: boolean
|
||||
|
||||
`true` if the node is a symlink.
|
||||
|
||||
### is_broken
|
||||
|
||||
Type: boolean
|
||||
|
||||
`true` if the node is a broken symlink.
|
||||
|
||||
### is_dir
|
||||
|
||||
Type: boolean
|
||||
|
||||
`true` if the node is a directory.
|
||||
|
||||
### is_file
|
||||
|
||||
Type: boolean
|
||||
|
||||
`true` if the node is a file.
|
||||
|
||||
### is_readonly
|
||||
|
||||
Type: boolean
|
||||
|
||||
`true` if the node is real-only.
|
||||
|
||||
### mime_essence
|
||||
|
||||
Type: string
|
||||
|
||||
The mime type of the node. For e.g. `text/csv`, `image/jpeg` etc.
|
||||
|
||||
### size
|
||||
|
||||
Type: integer
|
||||
|
||||
The size of the exact node. The size of a directory won't be calculated
|
||||
recursively.
|
||||
|
||||
### human_size
|
||||
|
||||
Type: string
|
||||
|
||||
Like size but in human readable format.
|
||||
|
||||
### permissions
|
||||
|
||||
Type: [Permission][60]
|
||||
|
||||
The [permissions][60] applied to the node.
|
||||
|
||||
### created
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
Creation time in nanosecond since UNIX epoch.
|
||||
|
||||
### last_modified
|
||||
|
||||
Type: nullable integer
|
||||
|
||||
Last modification time in nanosecond since UNIX epoch.
|
||||
|
||||
### uid
|
||||
|
||||
Type: integer
|
||||
|
||||
User ID of the file owner.
|
||||
|
||||
### gid
|
||||
|
||||
Type: integer
|
||||
|
||||
Group ID of the file owner.
|
||||
|
||||
### canonical
|
||||
|
||||
Type: nullable [Resolved Node Metadata][61]
|
||||
|
||||
If the node is a symlink, it will hold information about the symlink resolved
|
||||
node. Else, it will hold information the actual node. It the symlink is broken,
|
||||
it will be null.
|
||||
|
||||
### symlink
|
||||
|
||||
Type: nullable [Resolved Node Metadata][61]
|
||||
|
||||
If the node is a symlink and is not broken, it will hold information about the
|
||||
symlink resolved node. However, it will never hold information about the actual
|
||||
node. It will instead be null.
|
||||
|
||||
## Directory Buffer
|
||||
|
||||
Directory buffer contains the following fields:
|
||||
|
||||
- [parent][45]
|
||||
- [nodes][63]
|
||||
- [total][64]
|
||||
- [focus][65]
|
||||
|
||||
### parent
|
||||
|
||||
Type: string
|
||||
|
||||
The parent path of the node.
|
||||
|
||||
### nodes
|
||||
|
||||
Type: list of [Node][44]s
|
||||
|
||||
A list of visible nodes.
|
||||
|
||||
### total
|
||||
|
||||
Type: int
|
||||
|
||||
The count of nodes being rendered.
|
||||
|
||||
### focus
|
||||
|
||||
Type: int
|
||||
|
||||
The index of the node under focus. It can be `0` even when there's no node to
|
||||
focus on.
|
||||
|
||||
## History
|
||||
|
||||
History contains the following fields:
|
||||
|
||||
- [loc][68]
|
||||
- [paths][69]
|
||||
|
||||
### loc
|
||||
|
||||
Type: int
|
||||
|
||||
Location of the current path in history.
|
||||
|
||||
### paths
|
||||
|
||||
Type: list of string
|
||||
|
||||
Visited paths.
|
||||
|
||||
## Explorer Config
|
||||
|
||||
Explorer config contains the following fields:
|
||||
|
||||
- [filters][77]
|
||||
- [sorters][78]
|
||||
- [searcher][79]
|
||||
|
||||
### filters
|
||||
|
||||
List of filters to apply.
|
||||
|
||||
Type: list of [Node Filter Applicable][80]
|
||||
|
||||
### sorters
|
||||
|
||||
Add list or sorters to the pipeline.
|
||||
|
||||
Type: list of [Node Sorter Applicable][81]
|
||||
|
||||
### searcher
|
||||
|
||||
The searcher to use (if any).
|
||||
|
||||
Type: nullable [Node Searcher Applicable][82]
|
||||
|
||||
## Also See:
|
||||
|
||||
- [xplr.util][85]
|
||||
|
||||
[7]: https://www.json.org
|
||||
[8]: modes.md#mode
|
||||
[9]: modes.md#builtin
|
||||
[10]: modes.md#custom
|
||||
[11]: layouts.md
|
||||
[12]: layouts.md#builtin
|
||||
[13]: layouts.md#custom
|
||||
[14]: #lua-context
|
||||
[15]: filtering.md#filter
|
||||
[16]: filtering.md
|
||||
[17]: sorting.md#sorter
|
||||
[29]: #version
|
||||
[30]: #config
|
||||
[31]: #pwd
|
||||
[32]: #focused_node
|
||||
[33]: #directory_buffer
|
||||
[34]: #selection
|
||||
[35]: #mode
|
||||
[36]: #layout
|
||||
[37]: #input_buffer
|
||||
[38]: #pid
|
||||
[39]: #session_path
|
||||
[40]: #explorer_config
|
||||
[41]: #history
|
||||
[42]: #last_modes
|
||||
[43]: configuration.md#config
|
||||
[44]: #node
|
||||
[45]: #parent
|
||||
[46]: #relative_path
|
||||
[47]: #absolute_path
|
||||
[48]: #extension
|
||||
[49]: #is_symlink
|
||||
[50]: #is_broken
|
||||
[51]: #is_dir
|
||||
[52]: #is_file
|
||||
[53]: #is_readonly
|
||||
[54]: #mime_essence
|
||||
[55]: #size
|
||||
[56]: #human_size
|
||||
[57]: #permissions
|
||||
[58]: #canonical
|
||||
[59]: #symlink
|
||||
[60]: column-renderer.md#permission
|
||||
[61]: column-renderer.md#resolved-node-metadata
|
||||
[62]: #directory-buffer
|
||||
[63]: #nodes
|
||||
[64]: #total
|
||||
[65]: #focus
|
||||
[66]: #explorer-config
|
||||
[67]: #history
|
||||
[68]: #loc
|
||||
[69]: #paths
|
||||
[70]: #history-1
|
||||
[71]: #created
|
||||
[72]: #last_modified
|
||||
[73]: #uid
|
||||
[74]: #gid
|
||||
[75]: #vroot
|
||||
[76]: #initial_pwd
|
||||
[77]: #filters
|
||||
[78]: #sorters
|
||||
[79]: #searcher
|
||||
[80]: filtering.md#node-filter-applicable
|
||||
[81]: sorting.md#node-sorter-applicable
|
||||
[82]: searching.md#node-searcher-applicable
|
||||
[85]: xplr.util.md
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,66 @@
|
||||
# Mode
|
||||
|
||||
A mode contains the following information:
|
||||
|
||||
- [name][5]
|
||||
- [help][6]
|
||||
- [extra_help][7]
|
||||
- [key_bindings][9]
|
||||
- [layout][10]
|
||||
- [prompt][13]
|
||||
|
||||
### name
|
||||
|
||||
Type: string
|
||||
|
||||
This is the name of the mode visible in the help menu.
|
||||
|
||||
### help
|
||||
|
||||
Type: nullable string
|
||||
|
||||
If specified, the help menu will display this instead of the auto generated
|
||||
mappings.
|
||||
|
||||
### extra_help
|
||||
|
||||
Type: nullable string
|
||||
|
||||
If specified, the help menu will display this along-side the auto generated
|
||||
help menu.
|
||||
|
||||
### key_bindings
|
||||
|
||||
Type: [Key Bindings][8]
|
||||
|
||||
The key bindings available in that mode.
|
||||
|
||||
### layout
|
||||
|
||||
Type: nullable [Layout][11]
|
||||
|
||||
If specified, this layout will be used to render the UI.
|
||||
|
||||
### prompt
|
||||
|
||||
Type: nullable string
|
||||
|
||||
If set, this prompt will be displayed in the input buffer when in this mode.
|
||||
|
||||
## Also See:
|
||||
|
||||
- [xplr.config.modes][12]
|
||||
|
||||
[1]: #builtin
|
||||
[2]: #custom
|
||||
[3]: #mode
|
||||
[4]: default-key-bindings.md
|
||||
[5]: #name
|
||||
[6]: #help
|
||||
[7]: #extra_help
|
||||
[8]: configure-key-bindings.md#key-bindings
|
||||
[9]: #key_bindings
|
||||
[10]: #layout
|
||||
[11]: layout.md#layout
|
||||
[12]: modes.md
|
||||
[13]: #prompt
|
@ -0,0 +1,36 @@
|
||||
# Node Type
|
||||
|
||||
A node-type contains the following fields:
|
||||
|
||||
- [meta][4]
|
||||
- [style][5]
|
||||
|
||||
### meta
|
||||
|
||||
Type: mapping of string and string
|
||||
|
||||
A meta field can contain custom metadata about a node. By default, the "icon"
|
||||
metadata is set for the [directory][1], [file][2], and
|
||||
[symlink][3] nodes.
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.config.node_types.file = {
|
||||
meta = {
|
||||
icon = "f",
|
||||
foo = "bar",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Also See:
|
||||
|
||||
- [xplr.config.node_types][6]
|
||||
|
||||
[1]: node_types.md#directory
|
||||
[2]: node_types.md#file
|
||||
[3]: node_types.md#symlink
|
||||
[4]: #meta
|
||||
[5]: style.md
|
||||
[6]: node_types.md
|
@ -1,42 +1,27 @@
|
||||
Post Install
|
||||
============
|
||||
# Post Install
|
||||
|
||||
Once [installed][1], use the following steps to setup and run xplr.
|
||||
|
||||
Create the customizable config file
|
||||
-----------------------------------
|
||||
## Create the customizable config file
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/xplr
|
||||
|
||||
version="$(xplr | grep ^version: | cut -d' ' -f 2)"
|
||||
version="$(xplr --version | awk '{print $2}')"
|
||||
|
||||
# When the app loads, press `#`
|
||||
|
||||
echo version = '"'${version:?}'"' > ~/.config/xplr/init.lua
|
||||
echo "version = '${version:?}'" > ~/.config/xplr/init.lua
|
||||
```
|
||||
|
||||
Then
|
||||
**[copy from here][2]**
|
||||
and remove / comment out what you don't want to customize.
|
||||
|
||||
> **Note:** You don't generally need to create the config file. You can use the
|
||||
> default configuration for basic operations. However, creating the config file
|
||||
> is recommended because the project is in its early stage and the defaults
|
||||
> might change. Creating the config file will save you from unexpected behavior
|
||||
> when you [upgrade][3].
|
||||
> Also, the default configuration is meant to be overwritten to suit your
|
||||
> workflow.
|
||||
|
||||
|
||||
Run
|
||||
---
|
||||
## Run
|
||||
|
||||
```
|
||||
xplr
|
||||
```
|
||||
|
||||
|
||||
[1]:install.md
|
||||
[2]:https://github.com/sayanarijit/xplr/blob/main/src/init.lua
|
||||
[3]:upgrade-guide.md
|
||||
[1]: install.md
|
||||
[2]: https://github.com/sayanarijit/xplr/blob/main/src/init.lua
|
||||
[3]: upgrade-guide.md
|
||||
|
@ -1,12 +1,10 @@
|
||||
Quickstart
|
||||
==========
|
||||
# Quickstart
|
||||
|
||||
Nice to you have here! Let's quickly start our xplr journey with the
|
||||
Nice to you have here! Let's quickly start with the
|
||||
following steps:
|
||||
|
||||
- [Install][1]
|
||||
- [Post Install][2]
|
||||
|
||||
|
||||
[1]:install.md
|
||||
[2]:post-install.md
|
||||
[1]: install.md
|
||||
[2]: post-install.md
|
||||
|
@ -0,0 +1,77 @@
|
||||
# Searching
|
||||
|
||||
xplr supports searching paths using different algorithm. The search mechanism
|
||||
generally appears between filters and sorters in the `Sort & filter` panel.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
fzy:foo↓
|
||||
```
|
||||
|
||||
This line means that the nodes visible on the table are being filtered using the
|
||||
[fuzzy matching][1] algorithm on the input `foo`. The arrow means that ranking based
|
||||
ordering is being applied, i.e. [sorters][2] are being ignored.
|
||||
|
||||
## Node Searcher Applicable
|
||||
|
||||
Node Searcher contains the following fields:
|
||||
|
||||
- [pattern][3]
|
||||
- [recoverable_focus][4]
|
||||
- [algorithm][5]
|
||||
- [unordered][7]
|
||||
|
||||
### pattern
|
||||
|
||||
The patterns used to search.
|
||||
|
||||
Type: string
|
||||
|
||||
### recoverable_focus
|
||||
|
||||
Where to focus when search is cancelled.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
### algorithm
|
||||
|
||||
Search algorithm to use. Defaults to the value set in
|
||||
[xplr.config.general.search.algorithm][8].
|
||||
|
||||
It can be one of the following:
|
||||
|
||||
- Fuzzy
|
||||
- Regex
|
||||
|
||||
### unordered
|
||||
|
||||
Whether to skip ordering the search result by algorithm based ranking. Defaults
|
||||
to the value set in [xplr.config.general.search.unordered][9].
|
||||
|
||||
Type: boolean
|
||||
|
||||
## Example:
|
||||
|
||||
```lua
|
||||
local searcher = {
|
||||
pattern = "pattern to search",
|
||||
recoverable_focus = "/path/to/focus/on/cancel",
|
||||
algorithm = "Fuzzy",
|
||||
unordered = false,
|
||||
}
|
||||
|
||||
xplr.util.explore({ searcher = searcher })
|
||||
```
|
||||
|
||||
See [xplr.util.explore][6].
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Approximate_string_matching
|
||||
[2]: sorting.md
|
||||
[3]: #pattern
|
||||
[4]: #recoverable_focus
|
||||
[5]: #algorithm
|
||||
[6]: xplr.util.md#xplrutilexplore
|
||||
[7]: #unordered
|
||||
[8]: general-config.md#xplrconfiggeneralsearchalgorithm
|
||||
[9]: general-config.md#xplrconfiggeneralsearchunordered
|
@ -0,0 +1,96 @@
|
||||
# Sum Type
|
||||
|
||||
> This section isn't specific to xplr. However, since xplr configuration makes
|
||||
> heavy use of this particular data type, even though it isn't available in
|
||||
> most of the mainstream programming languages (yet), making it a wild or
|
||||
> unfamiliar concept for many, it's worth doing a quick introduction here.
|
||||
>
|
||||
> If you're already familiar with [Sum Type / Tagged Union][1] (e.g. Rust's
|
||||
> enum), you can skip ahead.
|
||||
|
||||
While reading this doc, you'll come across some data types like [Layout][2],
|
||||
[Color][4], [Message][3] etc. that says something like "x is a sum type that
|
||||
can be any of the following", and then you'll see a list of strings and/or lua
|
||||
tables just below.
|
||||
|
||||
Yes, they are actually sum types, i.e. they can be any of the given set of
|
||||
tagged variants listed there.
|
||||
|
||||
Notice the word "be". Unlike classes or structs (aka product types), they can't
|
||||
"have" values, they can only "be" the value, or rather, be one of the possible
|
||||
set of values.
|
||||
|
||||
Also notice the word "tagged". Unlike the single variant `null`, or the dual
|
||||
variant `boolean` types, the variants of sum types are tagged (i.e. named), and
|
||||
may further have, or be, value or values of any data type.
|
||||
|
||||
A simple example of a sum type is an enum. Many programming languages have
|
||||
them, but only a few modern programming languages allow nesting other types
|
||||
into a sum type.
|
||||
|
||||
```rust
|
||||
enum Color {
|
||||
Red,
|
||||
Green,
|
||||
}
|
||||
```
|
||||
|
||||
Here, `Color` can be one of two possible set of values: `Red` and `Green`, just
|
||||
like `boolean`, but unlike `boolean`, being tagged allows `Color` to have more
|
||||
than two variants if required, by changing the definition.
|
||||
|
||||
e.g.
|
||||
|
||||
```rust
|
||||
enum Color {
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
}
|
||||
```
|
||||
|
||||
We'd document it here as:
|
||||
|
||||
> Result is a sum type that can be one of the following:
|
||||
>
|
||||
> - "Red"
|
||||
> - "Green"
|
||||
> - "Blue"
|
||||
|
||||
But some languages (like Rust, Haskell, Elm etc.) go even further, allowing us
|
||||
to associate each branch of the enum with further nested types like:
|
||||
|
||||
```rust
|
||||
enum Layout {
|
||||
Table,
|
||||
HelpMenu,
|
||||
Horizontal {
|
||||
config: LayoutConfig, // A product type (similar to class/struct)
|
||||
splits: Vec<Layout> // A list of "Layout"s (i.e. list of sum types)
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Here, as we can see, unlike the first example, some of `Layout`'s possible
|
||||
variants can have further nested types associated with them. Note that
|
||||
`Horizontal` here can have a sum type (e.g. enum), or a product type (e.g.
|
||||
class/struct), or both (any number of them actually) nested in it. But the
|
||||
nested values will only exist when `Layout` is `Horizontal`.
|
||||
|
||||
We'd document it here as:
|
||||
|
||||
> Layout is a sum type that can be one of the following:
|
||||
>
|
||||
> - "Table"
|
||||
> - "HelpMenu"
|
||||
> - { Horizontal = { config = Layout Config, splits = { Layout, ... } }
|
||||
|
||||
And then we'd go on documenting whatever `Layout Config` is.
|
||||
|
||||
So, there you go. This is exactly what sum types are - glorified enums that can
|
||||
have nested types in each branch.
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Tagged_union
|
||||
[2]: layout.md
|
||||
[3]: message.md
|
||||
[4]: style.md#color
|
@ -1,36 +0,0 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- [x] Saner key bindings.
|
||||
- [x] Pipes.
|
||||
- [x] Native search & filter.
|
||||
- [x] Create, copy, move, delete files directly.
|
||||
- [x] logging support.
|
||||
- [x] Version compatibility instructions.
|
||||
- [x] Implement CLI arguments.
|
||||
- ~Add support for tabs and/or panes (non native)~ [hacked][1] | [discussion][2]
|
||||
- ~Implement bookmarks.~ [hacked][3]
|
||||
- [x] Add sorting support.
|
||||
- [x] Add filter support.
|
||||
- [x] File previews.
|
||||
- [x] Implement plugins support (or some way to easily share configuration).
|
||||
- [x] Bigger (and better) help menu.
|
||||
- [x] Offline docs.
|
||||
- [ ] Support for background services
|
||||
- [x] ~Customize~ switch UI at run-time.
|
||||
- [ ] More tests and benchmarks.
|
||||
- [ ] Measure code coverage.
|
||||
- [ ] Improve the [vim plugin][4].
|
||||
- [ ] Cleanup, refactor, optimize.
|
||||
|
||||
[add more][5]
|
||||
|
||||
Like this project so far? **[Please consider contributing][6].**
|
||||
|
||||
|
||||
[1]:https://github.com/sayanarijit/xplr/wiki/Hacks#spawn-multiple-sessions-in-different-windows
|
||||
[2]:https://github.com/sayanarijit/xplr/discussions/15
|
||||
[3]:https://github.com/sayanarijit/xplr/wiki/Hacks#bookmark
|
||||
[4]:https://github.com/sayanarijit/xplr.vim
|
||||
[5]:https://github.com/sayanarijit/xplr/discussions/2
|
||||
[6]:contribute.md
|
@ -0,0 +1,528 @@
|
||||
### xplr.util.version
|
||||
|
||||
Get the xplr version details.
|
||||
|
||||
Type: function() -> { major: number, minor: number, patch: number }
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.version()
|
||||
-- { major = 0, minor = 0, patch = 0 }
|
||||
```
|
||||
|
||||
### xplr.util.debug
|
||||
|
||||
Print the given value to the console, and return it as a string.
|
||||
Useful for debugging.
|
||||
|
||||
Type: function( value ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.debug({ foo = "bar", bar = function() end })
|
||||
-- {
|
||||
-- ["bar"] = function: 0x55e5cebdeae0,
|
||||
-- ["foo"] = "bar",
|
||||
-- }
|
||||
```
|
||||
|
||||
### xplr.util.clone
|
||||
|
||||
Clone/deepcopy a Lua value. Doesn't work with functions.
|
||||
|
||||
Type: function( value ) -> value
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
local val = { foo = "bar" }
|
||||
local val_clone = xplr.util.clone(val)
|
||||
val.foo = "baz"
|
||||
print(val_clone.foo)
|
||||
-- "bar"
|
||||
```
|
||||
|
||||
### xplr.util.exists
|
||||
|
||||
Check if the given path exists.
|
||||
|
||||
Type: function( path:string ) -> boolean
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.exists("/foo/bar")
|
||||
-- true
|
||||
```
|
||||
|
||||
### xplr.util.is_dir
|
||||
|
||||
Check if the given path is a directory.
|
||||
|
||||
Type: function( path:string ) -> boolean
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.is_dir("/foo/bar")
|
||||
-- true
|
||||
```
|
||||
|
||||
### xplr.util.is_file
|
||||
|
||||
Check if the given path is a file.
|
||||
|
||||
Type: function( path:string ) -> boolean
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.is_file("/foo/bar")
|
||||
-- true
|
||||
```
|
||||
|
||||
### xplr.util.is_symlink
|
||||
|
||||
Check if the given path is a symlink.
|
||||
|
||||
Type: function( path:string ) -> boolean
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.is_file("/foo/bar")
|
||||
-- true
|
||||
```
|
||||
|
||||
### xplr.util.is_absolute
|
||||
|
||||
Check if the given path is an absolute path.
|
||||
|
||||
Type: function( path:string ) -> boolean
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.is_absolute("/foo/bar")
|
||||
-- true
|
||||
```
|
||||
|
||||
### xplr.util.path_split
|
||||
|
||||
Split a path into its components.
|
||||
|
||||
Type: function( path:string ) -> boolean
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.path_split("/foo/bar")
|
||||
-- { "/", "foo", "bar" }
|
||||
|
||||
xplr.util.path_split(".././foo")
|
||||
-- { "..", "foo" }
|
||||
```
|
||||
|
||||
### xplr.util.node
|
||||
|
||||
Get [Node][5] information of a given path.
|
||||
Doesn't check if the path exists.
|
||||
Returns nil if the path is "/".
|
||||
Errors out if absolute path can't be obtained.
|
||||
|
||||
Type: function( path:string ) -> [Node][5]|nil
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.node("./bar")
|
||||
-- { parent = "/pwd", relative_path = "bar", absolute_path = "/pwd/bar", ... }
|
||||
|
||||
xplr.util.node("/")
|
||||
-- nil
|
||||
```
|
||||
|
||||
### xplr.util.node_type
|
||||
|
||||
Get the configured [Node Type][6] of a given [Node][5].
|
||||
|
||||
Type: function( [Node][5], [xplr.config.node_types][7]|nil ) -> [Node Type][6]
|
||||
|
||||
If the second argument is missing, global config `xplr.config.node_types`
|
||||
will be used.
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.node_type(app.focused_node)
|
||||
-- { style = { fg = "Red", ... }, meta = { icon = "", ... } ... }
|
||||
|
||||
xplr.util.node_type(xplr.util.node("/foo/bar"), xplr.config.node_types)
|
||||
-- { style = { fg = "Red", ... }, meta = { icon = "", ... } ... }
|
||||
```
|
||||
|
||||
### xplr.util.dirname
|
||||
|
||||
Get the directory name of a given path.
|
||||
|
||||
Type: function( path:string ) -> path:string|nil
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.dirname("/foo/bar")
|
||||
-- "/foo"
|
||||
```
|
||||
|
||||
### xplr.util.basename
|
||||
|
||||
Get the base name of a given path.
|
||||
|
||||
Type: function( path:string ) -> path:string|nil
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.basename("/foo/bar")
|
||||
-- "bar"
|
||||
```
|
||||
|
||||
### xplr.util.absolute
|
||||
|
||||
Get the absolute path of the given path by prepending $PWD.
|
||||
It doesn't check if the path exists.
|
||||
|
||||
Type: function( path:string ) -> path:string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.absolute("foo/bar")
|
||||
-- "/tmp/foo/bar"
|
||||
```
|
||||
|
||||
### xplr.util.relative_to
|
||||
|
||||
Get the relative path based on the given base path or current working dir.
|
||||
Will error if it fails to determine a relative path.
|
||||
|
||||
Type: function( path:string, options:table|nil ) -> path:string
|
||||
|
||||
Options type: { base:string|nil, with_prefix_dots:bookean|nil, without_suffix_dots:boolean|nil }
|
||||
|
||||
- If `base` path is given, the path will be relative to it.
|
||||
- If `with_prefix_dots` is true, the path will always start with dots `..` / `.`
|
||||
- If `without_suffix_dots` is true, the name will be visible instead of dots `..` / `.`
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.relative_to("/present/working/directory")
|
||||
-- "."
|
||||
|
||||
xplr.util.relative_to("/present/working/directory/foo")
|
||||
-- "foo"
|
||||
|
||||
xplr.util.relative_to("/present/working/directory/foo", { with_prefix_dots = true })
|
||||
-- "./foo"
|
||||
|
||||
xplr.util.relative_to("/present/working/directory", { without_suffix_dots = true })
|
||||
-- "../directory"
|
||||
|
||||
xplr.util.relative_to("/present/working")
|
||||
-- ".."
|
||||
|
||||
xplr.util.relative_to("/present/working", { without_suffix_dots = true })
|
||||
-- "../../working"
|
||||
|
||||
xplr.util.relative_to("/present/working/directory", { base = "/present/foo/bar" })
|
||||
-- "../../working/directory"
|
||||
```
|
||||
|
||||
### xplr.util.shorten
|
||||
|
||||
Shorten the given absolute path using the following rules:
|
||||
|
||||
- either relative to your home dir if it makes sense
|
||||
- or relative to the current working directory
|
||||
- or absolute path if it makes the most sense
|
||||
|
||||
Type: Similar to `xplr.util.relative_to`
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.shorten("/home/username/.config")
|
||||
-- "~/.config"
|
||||
|
||||
xplr.util.shorten("/present/working/directory")
|
||||
-- "."
|
||||
|
||||
xplr.util.shorten("/present/working/directory/foo")
|
||||
-- "foo"
|
||||
|
||||
xplr.util.shorten("/present/working/directory/foo", { with_prefix_dots = true })
|
||||
-- "./foo"
|
||||
|
||||
xplr.util.shorten("/present/working/directory", { without_suffix_dots = true })
|
||||
-- "../directory"
|
||||
|
||||
xplr.util.shorten("/present/working/directory", { base = "/present/foo/bar" })
|
||||
-- "../../working/directory"
|
||||
|
||||
xplr.util.shorten("/tmp")
|
||||
-- "/tmp"
|
||||
```
|
||||
|
||||
### xplr.util.explore
|
||||
|
||||
Explore directories with the given explorer config.
|
||||
|
||||
Type: function( path:string, [ExplorerConfig][1]|nil ) -> { [Node][2], ... }
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
|
||||
xplr.util.explore("/tmp")
|
||||
-- { { absolute_path = "/tmp/a", ... }, ... }
|
||||
|
||||
xplr.util.explore("/tmp", app.explorer_config)
|
||||
-- { { absolute_path = "/tmp/a", ... }, ... }
|
||||
```
|
||||
|
||||
### xplr.util.shell_execute
|
||||
|
||||
Execute shell commands safely.
|
||||
|
||||
Type: function( program:string, args:{ string, ... }|nil ) -> { stdout = string, stderr = string, returncode = number|nil }
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.shell_execute("pwd")
|
||||
-- { stdout = "/present/working/directory", stderr = "", returncode = 0 }
|
||||
|
||||
xplr.util.shell_execute("bash", {"-c", "xplr --help"})
|
||||
-- { stdout = "xplr...", stderr = "", returncode = 0 }
|
||||
```
|
||||
|
||||
### xplr.util.shell_quote
|
||||
|
||||
Quote commands and paths safely.
|
||||
|
||||
Type: function( string ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.shell_quote("a'b\"c")
|
||||
-- 'a'"'"'b"c'
|
||||
```
|
||||
|
||||
### xplr.util.shell_escape
|
||||
|
||||
Escape commands and paths safely.
|
||||
|
||||
Type: function( string ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.shell_escape("a'b\"c")
|
||||
-- "\"a'b\\\"c\""
|
||||
```
|
||||
|
||||
### xplr.util.from_json
|
||||
|
||||
Load JSON string into Lua value.
|
||||
|
||||
Type: function( string ) -> any
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.from_json([[{"foo": "bar"}]])
|
||||
-- { foo = "bar" }
|
||||
```
|
||||
|
||||
### xplr.util.to_json
|
||||
|
||||
Dump Lua value into JSON (i.e. also YAML) string.
|
||||
|
||||
Type: function( value ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.to_json({ foo = "bar" })
|
||||
-- [[{ "foo": "bar" }]]
|
||||
|
||||
xplr.util.to_json({ foo = "bar" }, { pretty = true })
|
||||
-- [[{
|
||||
-- "foo": "bar"
|
||||
-- }]]
|
||||
```
|
||||
|
||||
### xplr.util.from_yaml
|
||||
|
||||
Load YAML (i.e. also JSON) string into Lua value.
|
||||
|
||||
Type: function( string ) -> value
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.from_yaml([[{foo: bar}]])
|
||||
-- { foo = "bar" }
|
||||
```
|
||||
|
||||
### xplr.util.to_yaml
|
||||
|
||||
Dump Lua value into YAML string.
|
||||
|
||||
Type: function( value ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.to_yaml({ foo = "bar" })
|
||||
-- "foo: bar"
|
||||
```
|
||||
|
||||
### xplr.util.lscolor
|
||||
|
||||
Get a [Style][3] object for the given path based on the LS_COLORS
|
||||
environment variable.
|
||||
|
||||
Type: function( path:string ) -> [Style][3]
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.lscolor("Desktop")
|
||||
-- { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} }
|
||||
```
|
||||
|
||||
### xplr.util.paint
|
||||
|
||||
Apply style (escape sequence) to string using a given [Style][3] object.
|
||||
|
||||
Type: function( string, [Style][3]|nil ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.paint("Desktop", { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} })
|
||||
-- "\u001b[31mDesktop\u001b[0m"
|
||||
```
|
||||
|
||||
### xplr.util.style_mix
|
||||
|
||||
Mix multiple [Style][3] objects into one.
|
||||
|
||||
Type: function( { [Style][3], [Style][3], ... } ) -> [Style][3]
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.style_mix({{ fg = "Red" }, { bg = "Blue" }, { add_modifiers = {"Bold"} }})
|
||||
-- { fg = "Red", bg = "Blue", add_modifiers = { "Bold" }, sub_modifiers = {} }
|
||||
```
|
||||
|
||||
### xplr.util.textwrap
|
||||
|
||||
Wrap the given text to fit the specified width.
|
||||
It will try to not split words when possible.
|
||||
|
||||
Type: function( string, options:number|table ) -> { string, ...}
|
||||
|
||||
Options type: { width = number, initial_indent = string|nil, subsequent_indent = string|nil, break_words = boolean|nil }
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.textwrap("this will be cut off", 11)
|
||||
-- { "this will', 'be cut off" }
|
||||
|
||||
xplr.util.textwrap(
|
||||
"this will be cut off",
|
||||
{ width = 12, initial_indent = "", subsequent_indent = " ", break_words = false }
|
||||
)
|
||||
-- { "this will be", " cut off" }
|
||||
```
|
||||
|
||||
### xplr.util.layout_replace
|
||||
|
||||
Find the target layout in the given layout and replace it with the replacement layout,
|
||||
returning a new layout.
|
||||
|
||||
Type: function( layout:[Layout][4], target:[Layout][4], replacement:[Layout][4] ) -> layout:[Layout][4]
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
local layout = {
|
||||
Horizontal = {
|
||||
splits = {
|
||||
"Table", -- Target
|
||||
"HelpMenu",
|
||||
},
|
||||
config = ...,
|
||||
}
|
||||
}
|
||||
|
||||
xplr.util.layout_replace(layout, "Table", "Selection")
|
||||
-- {
|
||||
-- Horizontal = {
|
||||
-- splits = {
|
||||
-- "Selection", -- Replacement
|
||||
-- "HelpMenu",
|
||||
-- },
|
||||
-- config = ...
|
||||
-- }
|
||||
-- }
|
||||
```
|
||||
|
||||
### xplr.util.permissions_rwx
|
||||
|
||||
Convert [Permission][8] to rwxrwxrwx representation with special bits.
|
||||
|
||||
Type: function( [Permission][8] ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.permissions_rwx({ user_read = true })
|
||||
-- "r--------"
|
||||
|
||||
xplr.util.permissions_rwx(app.focused_node.permission)
|
||||
-- "rwxrwsrwT"
|
||||
```
|
||||
|
||||
### xplr.util.permissions_octal
|
||||
|
||||
Convert [Permission][8] to octal representation.
|
||||
|
||||
Type: function( [Permission][8] ) -> { number, number, number, number }
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.permissions_octal({ user_read = true })
|
||||
-- { 0, 4, 0, 0 }
|
||||
|
||||
xplr.util.permissions_octal(app.focused_node.permission)
|
||||
-- { 0, 7, 5, 4 }
|
||||
```
|
||||
|
||||
[1]: https://xplr.dev/en/lua-function-calls#explorer-config
|
||||
[2]: https://xplr.dev/en/lua-function-calls#node
|
||||
[3]: https://xplr.dev/en/style
|
||||
[4]: https://xplr.dev/en/layout
|
||||
[5]: https://xplr.dev/en/lua-function-calls#node
|
||||
[6]: https://xplr.dev/en/node-type
|
||||
[7]: https://xplr.dev/en/node_types
|
||||
[8]: https://xplr.dev/en/column-renderer#permission
|
Binary file not shown.
After Width: | Height: | Size: 789 B |
@ -0,0 +1,357 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="{{ language }}" class="{{ default_theme }}" dir="{{ text_direction }}">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ title }}</title>
|
||||
{{#if is_print }}
|
||||
<meta name="robots" content="noindex">
|
||||
{{/if}}
|
||||
{{#if base_url}}
|
||||
<base href="{{ base_url }}">
|
||||
{{/if}}
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
{{> head}}
|
||||
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
{{#if favicon_svg}}
|
||||
<link rel="icon" href="{{ path_to_root }}favicon.svg">
|
||||
{{/if}}
|
||||
{{#if favicon_png}}
|
||||
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
|
||||
{{/if}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
|
||||
{{#if print_enable}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
|
||||
{{/if}}
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
|
||||
{{#if copy_fonts}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
|
||||
{{/if}}
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
{{#each additional_css}}
|
||||
<link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
|
||||
{{/each}}
|
||||
|
||||
{{#if mathjax_support}}
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
{{/if}}
|
||||
|
||||
<!-- EthicalAds -->
|
||||
<script async src="https://media.ethicalads.io/media/client/ethicalads.min.js"></script>
|
||||
</head>
|
||||
<body class="sidebar-visible no-js">
|
||||
<div id="body-container">
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "{{ path_to_root }}";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('{{ default_theme }}')
|
||||
html.classList.add(theme);
|
||||
var body = document.querySelector('body');
|
||||
body.classList.remove('no-js')
|
||||
body.classList.add('js');
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var body = document.querySelector('body');
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
body.classList.remove('sidebar-visible');
|
||||
body.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
{{#toc}}{{/toc}}
|
||||
|
||||
<!-- EthicalAds -->
|
||||
<div
|
||||
id="docs-sidebar-bottom"
|
||||
class="dark flat"
|
||||
data-ea-publisher="xplrdev"
|
||||
data-ea-type="image"
|
||||
></div>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Track and set sidebar scroll position -->
|
||||
<script>
|
||||
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
||||
sidebarScrollbox.addEventListener('click', function(e) {
|
||||
if (e.target.tagName === 'A') {
|
||||
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
||||
}
|
||||
}, { passive: true });
|
||||
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
||||
sessionStorage.removeItem('sidebar-scroll');
|
||||
if (sidebarScrollTop) {
|
||||
// preserve sidebar scroll position when navigating via links within sidebar
|
||||
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
||||
} else {
|
||||
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
||||
var activeSection = document.querySelector('#sidebar .active');
|
||||
if (activeSection) {
|
||||
activeSection.scrollIntoView({ block: 'center' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
{{> header}}
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
{{#if search_enabled}}
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
{{#if print_enable}}
|
||||
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if git_repository_url}}
|
||||
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{#if git_repository_edit_url}}
|
||||
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
|
||||
<i id="git-edit-button" class="fa fa-edit"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if search_enabled}}
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
{{{ content }}}
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
{{#if live_reload_endpoint}}
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script>
|
||||
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if google_analytics}}
|
||||
<!-- Google Analytics Tag -->
|
||||
<script>
|
||||
var localAddrs = ["localhost", "127.0.0.1", ""];
|
||||
|
||||
// make sure we don't activate google analytics if the developer is
|
||||
// inspecting the book locally...
|
||||
if (localAddrs.indexOf(document.location.hostname) === -1) {
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', '{{google_analytics}}', 'auto');
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_line_numbers}}
|
||||
<script>
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_copyable}}
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if playground_js}}
|
||||
<script src="{{ path_to_root }}ace.js"></script>
|
||||
<script src="{{ path_to_root }}editor.js"></script>
|
||||
<script src="{{ path_to_root }}mode-rust.js"></script>
|
||||
<script src="{{ path_to_root }}theme-dawn.js"></script>
|
||||
<script src="{{ path_to_root }}theme-tomorrow_night.js"></script>
|
||||
{{/if}}
|
||||
|
||||
{{#if search_js}}
|
||||
<script src="{{ path_to_root }}elasticlunr.min.js"></script>
|
||||
<script src="{{ path_to_root }}mark.min.js"></script>
|
||||
<script src="{{ path_to_root }}searcher.js"></script>
|
||||
{{/if}}
|
||||
|
||||
<script src="{{ path_to_root }}clipboard.min.js"></script>
|
||||
<script src="{{ path_to_root }}highlight.js"></script>
|
||||
<script src="{{ path_to_root }}book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
{{#each additional_js}}
|
||||
<script src="{{ ../path_to_root }}{{this}}"></script>
|
||||
{{/each}}
|
||||
|
||||
{{#if is_print}}
|
||||
{{#if mathjax_support}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
MathJax.Hub.Register.StartupHook('End', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{else}}
|
||||
<script>
|
||||
window.addEventListener('load', function() {
|
||||
window.setTimeout(window.print, 100);
|
||||
});
|
||||
</script>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,109 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html itemscope itemtype="https://schema.org/Product">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no"
|
||||
/>
|
||||
|
||||
<title>xplr</title>
|
||||
<meta name="title" content="xplr" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:url" content="https://xplr.dev/" />
|
||||
<meta name="twitter:title" content="xplr" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://s3.gifyu.com/images/imaged284d8b830767ac4.png"
|
||||
/>
|
||||
<meta name="twitter:site" content="https://xplr.dev" />
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://xplr.dev/" />
|
||||
<meta property="og:title" content="xplr" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://s3.gifyu.com/images/imaged284d8b830767ac4.png"
|
||||
/>
|
||||
<meta
|
||||
property="og:image:alt"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
|
||||
<meta itemprop="name" content="xplr" />
|
||||
<meta
|
||||
itemprop="description"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="https://s3.gifyu.com/images/icon2194563908b29554.png"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&display=swap');
|
||||
|
||||
body,
|
||||
iframe {
|
||||
max-width: 840px;
|
||||
margin: auto;
|
||||
font-family: 'Share Tech Mono', monospace;
|
||||
font-size: x-large;
|
||||
line-height: 1.5;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
iframe,
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#content {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<h1>xplr</h1>
|
||||
<p>A hackable, minimal, fast TUI file explorer</p>
|
||||
|
||||
<a href="https://github.com/sayanarijit/xplr"
|
||||
><img
|
||||
src="https://s3.gifyu.com/images/xplr-0.5.0.gif"
|
||||
alt="xplr demo"
|
||||
border="0"
|
||||
/></a>
|
||||
|
||||
<p class="links">
|
||||
<a href="en">Documentation</a>
|
||||
|
|
||||
<a href="https://github.com/sayanarijit/xplr">Project Repository</a>
|
||||
|
|
||||
<a href="https://github.com/sayanarijit/xplr/releases">Releases</a>
|
||||
|
|
||||
<a href="https://discord.gg/JmasSPCcz3">Discord</a>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1 @@
|
||||
../../assets
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,235 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<title>xplr - A hackable, minimal, fast TUI file explorer</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="xplr is a hackable, minimal, fast TUI file explorer "
|
||||
/>
|
||||
|
||||
<!--Inter UI font-->
|
||||
<link href="https://rsms.me/inter/inter.css" rel="stylesheet" />
|
||||
|
||||
<!--vendors styles-->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick-theme.min.css"
|
||||
/>
|
||||
|
||||
<!-- Icons -->
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="126x128"
|
||||
href="/assets/icon/xplr128.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="64x64"
|
||||
href="/assets/icon/xplr64.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/assets/icon/xplr32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/assets/icon/xplr16.png"
|
||||
/>
|
||||
|
||||
<!-- Bootstrap CSS / Color Scheme -->
|
||||
<link rel="stylesheet" href="css/default.css" id="theme-color" />
|
||||
|
||||
<!-- ------------- SEO TAGS --------------------->
|
||||
<meta name="title" content="xplr" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
|
||||
<meta name="twitter:title" content="xplr" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://s8.gifyu.com/images/xplr-0.17.6.jpg"
|
||||
/>
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:url" content="https://xplr.dev" />
|
||||
|
||||
<meta property="og:title" content="xplr" />
|
||||
<meta property="og:type" content="object" />
|
||||
<meta property="og:url" content="https://xplr.dev" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://s8.gifyu.com/images/xplr-0.17.6.jpg"
|
||||
/>
|
||||
|
||||
<meta itemprop="name" content="xplr" />
|
||||
<meta
|
||||
itemprop="description"
|
||||
content="A hackable, minimal, fast TUI file explorer"
|
||||
/>
|
||||
|
||||
<!-- Youtube Embed -->
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/paulirish/lite-youtube-embed@master/src/lite-yt-embed.min.css"
|
||||
/>
|
||||
|
||||
<script
|
||||
defer="defer"
|
||||
src="https://cdn.jsdelivr.net/gh/paulirish/lite-youtube-embed@master/src/lite-yt-embed.min.js"
|
||||
></script>
|
||||
|
||||
<!-- EthicalAds by "Read The Docs" folks -->
|
||||
<script
|
||||
async
|
||||
src="https://media.ethicalads.io/media/client/ethicalads.min.js"
|
||||
></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--navigation-->
|
||||
<section>
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-expand-md navbar-dark">
|
||||
<a class="navbar-brand heading-black" href="/">
|
||||
<img src="/assets/icon/xplr32.png" alt="xplr" />
|
||||
</a>
|
||||
<button
|
||||
class="navbar-toggler navbar-toggler-right border-0"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#navbarCollapse"
|
||||
aria-controls="navbarCollapse"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span data-feather="grid"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link page-scroll"
|
||||
href="/en/introduction.html#features"
|
||||
>
|
||||
Features
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link page-scroll" href="/en">Documentation</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link page-scroll" href="/en/community.html">
|
||||
Community
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link page-scroll"
|
||||
href="https://blog.xplr.dev"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link page-scroll"
|
||||
href="https://github.com/sayanarijit/xplr"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<hr />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<div
|
||||
id="landing-under-navbar"
|
||||
class="dark flat"
|
||||
data-ea-publisher="xplrdev"
|
||||
data-ea-type="text"
|
||||
></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--hero header-->
|
||||
<section id="home">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-sm-10 col-12 mx-auto my-auto text-center">
|
||||
<h1 class="heading-black">xplr</h1>
|
||||
<p class="lead">A hackable, minimal, fast TUI file explorer</p>
|
||||
|
||||
<div id="QJaEMeVo9Uw" class="eleventy-plugin-youtube-embed pb-3">
|
||||
<lite-youtube
|
||||
videoid="QJaEMeVo9Uw"
|
||||
style="
|
||||
background-image: url('https://s8.gifyu.com/images/xplr-0.17.6.jpg');
|
||||
"
|
||||
>
|
||||
<div class="lty-playbtn"></div>
|
||||
</lite-youtube>
|
||||
</div>
|
||||
|
||||
<a
|
||||
class="btn btn-primary d-inline-flex flex-row align-items-center py-3"
|
||||
href="/en/install.html"
|
||||
class="mb-3"
|
||||
>
|
||||
Try or Install
|
||||
<em class="ml-2" data-feather="arrow-right"></em>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!--scroll to top-->
|
||||
<div class="scroll-top">
|
||||
<i class="fa fa-angle-up" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/feather-icons/4.7.3/feather.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>
|
||||
<script src="js/scripts.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,73 @@
|
||||
$(function () {
|
||||
|
||||
// init feather icons
|
||||
feather.replace();
|
||||
|
||||
// init tooltip & popovers
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
||||
//page scroll
|
||||
$('a.page-scroll').bind('click', function (event) {
|
||||
var $anchor = $(this);
|
||||
$('html, body').stop().animate({
|
||||
scrollTop: $($anchor.attr('href')).offset().top - 20
|
||||
}, 1000);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// slick slider
|
||||
$('.slick-about').slick({
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
autoplay: true,
|
||||
autoplaySpeed: 3000,
|
||||
dots: true,
|
||||
arrows: false
|
||||
});
|
||||
|
||||
//toggle scroll menu
|
||||
var scrollTop = 0;
|
||||
$(window).scroll(function () {
|
||||
var scroll = $(window).scrollTop();
|
||||
//adjust menu background
|
||||
if (scroll > 80) {
|
||||
if (scroll > scrollTop) {
|
||||
$('.smart-scroll').addClass('scrolling').removeClass('up');
|
||||
} else {
|
||||
$('.smart-scroll').addClass('up');
|
||||
}
|
||||
} else {
|
||||
// remove if scroll = scrollTop
|
||||
$('.smart-scroll').removeClass('scrolling').removeClass('up');
|
||||
}
|
||||
|
||||
scrollTop = scroll;
|
||||
|
||||
// adjust scroll to top
|
||||
if (scroll >= 600) {
|
||||
$('.scroll-top').addClass('active');
|
||||
} else {
|
||||
$('.scroll-top').removeClass('active');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// scroll top top
|
||||
$('.scroll-top').click(function () {
|
||||
$('html, body').stop().animate({
|
||||
scrollTop: 0
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
/**Theme switcher - DEMO PURPOSE ONLY */
|
||||
$('.switcher-trigger').click(function () {
|
||||
$('.switcher-wrap').toggleClass('active');
|
||||
});
|
||||
$('.color-switcher ul li').click(function () {
|
||||
var color = $(this).attr('data-color');
|
||||
$('#theme-color').attr("href", "css/" + color + ".css");
|
||||
$('.color-switcher ul li').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
});
|
@ -0,0 +1,561 @@
|
||||
/*!
|
||||
* Created by Kroplet (https://www.kroplet.com)
|
||||
* The easiest way to create Bootstrap 4 themes.
|
||||
*/
|
||||
|
||||
/*****************
|
||||
Custom CSS
|
||||
*****************/
|
||||
body {
|
||||
border-color: $black;
|
||||
}
|
||||
.small-xl {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.img-faded {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.font-weight-medium {
|
||||
font-weight: 600;
|
||||
}
|
||||
.heading-black {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* Box shadow */
|
||||
.btn {
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
&.btn-#{$color} {
|
||||
box-shadow: 0 8px 16px rgba($value, 0.3);
|
||||
transition: all 0.2s ease-out;
|
||||
&:hover {
|
||||
box-shadow: 0 8px 20px rgba($value, 0.35);
|
||||
}
|
||||
&:active {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Navbar*/
|
||||
.navbar {
|
||||
&.navbar-transparent {
|
||||
opacity: 0.98;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
&.navbar-transparent {
|
||||
background-color: rgba($black, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 900;
|
||||
color: $primary;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.navbar-nav .nav-item {
|
||||
margin: 0 0.7rem;
|
||||
|
||||
.nav-link {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section-angle {
|
||||
position: relative;
|
||||
border-color: inherit;
|
||||
background: $black;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
content: '';
|
||||
left: 0;
|
||||
border: 0 solid transparent;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&.top-left:before,
|
||||
&.top-right:before {
|
||||
top: 0;
|
||||
border-left-width: 100vw;
|
||||
}
|
||||
|
||||
&.bottom-left:after,
|
||||
&.bottom-right:after {
|
||||
bottom: 0;
|
||||
border-right-width: 100vw;
|
||||
}
|
||||
|
||||
&.bottom-left:after {
|
||||
border-right-color: inherit;
|
||||
}
|
||||
|
||||
&.bottom-right:after {
|
||||
border-bottom-color: inherit;
|
||||
}
|
||||
|
||||
&.top-left:before {
|
||||
border-top-color: inherit;
|
||||
}
|
||||
|
||||
&.top-right:before {
|
||||
border-left-color: inherit;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up('lg') {
|
||||
&.bottom-right:after,
|
||||
&.top-right:before {
|
||||
border-bottom-width: 2rem;
|
||||
}
|
||||
&.bottom-left:after,
|
||||
&.top-left:before {
|
||||
border-top-width: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down('sm') {
|
||||
&.bottom-right:after,
|
||||
&.top-right:before {
|
||||
border-bottom-width: 1rem;
|
||||
}
|
||||
&.bottom-left:after,
|
||||
&.top-left:before {
|
||||
border-top-width: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*smart scrolling*/
|
||||
.smart-scroll {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1020;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease-out;
|
||||
|
||||
&.scrolling {
|
||||
transform: translateY(-100%);
|
||||
|
||||
&.up {
|
||||
background-color: rgba($black, 0.9);
|
||||
transform: translateY(0);
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**dividers */
|
||||
.divider {
|
||||
position: relative;
|
||||
|
||||
&.top-divider:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: block;
|
||||
height: 1px;
|
||||
background: $gray-900;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($primary, 0.1) 0,
|
||||
$gray-900 50%,
|
||||
rgba($primary, 0.1) 100%
|
||||
);
|
||||
}
|
||||
|
||||
&.bottom-divider:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: block;
|
||||
height: 1px;
|
||||
background: $gray-900;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba($primary, 0.24) 0,
|
||||
$gray-900 50%,
|
||||
rgba($primary, 0.24) 100%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*vertical heights */
|
||||
.vh-100 {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.vh-sm-100 {
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.vh-md-100 {
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-hero {
|
||||
background-color: $black;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 800 800'%3E%3Cg %3E%3Ccircle fill='#{$black}' cx='400' cy='400' r='600'/%3E%3Ccircle fill='#{darken($primary,40%)}' cx='400' cy='400' r='500'/%3E%3Ccircle fill='#{darken($primary,30%)}' cx='400' cy='400' r='400'/%3E%3Ccircle fill='#{darken($primary,20%)}' cx='400' cy='400' r='300'/%3E%3Ccircle fill='#{darken($primary,10%)}' cx='400' cy='400' r='200'/%3E%3Ccircle fill='#{$primary}' cx='400' cy='400' r='100'/%3E%3C/g%3E%3C/svg%3E");
|
||||
/* background by SVGBackgrounds.com */
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
content: '';
|
||||
background-color: rgba($black, 0.7);
|
||||
}
|
||||
|
||||
.container {
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
/*features boxes*/
|
||||
.feature-boxes {
|
||||
text-align: center;
|
||||
|
||||
.box {
|
||||
padding: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
/*features-list */
|
||||
.features-list {
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
float: left;
|
||||
width: 50%;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.75rem;
|
||||
font-size: 1.05rem;
|
||||
padding-left: 1.75rem;
|
||||
font-weight: 500;
|
||||
|
||||
&:before {
|
||||
position: relative;
|
||||
font-family: FontAwesome;
|
||||
font-size: 14px;
|
||||
content: '\f10c';
|
||||
color: $primary;
|
||||
margin: 0 0.75rem 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Pricing tables*/
|
||||
.pricing-table {
|
||||
.pricing-list {
|
||||
margin-bottom: 3rem;
|
||||
margin-top: 2rem;
|
||||
li {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
transition: all 0.25s ease-out;
|
||||
text-align: center;
|
||||
|
||||
.card-body {
|
||||
padding: 2.25rem 2rem;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**slick slider */
|
||||
.slick-dots {
|
||||
top: -4rem;
|
||||
|
||||
li {
|
||||
button::before {
|
||||
font-size: 0.75rem !important;
|
||||
line-height: 3.5rem !important;
|
||||
}
|
||||
|
||||
button::before,
|
||||
&.slick-active button:before {
|
||||
color: $primary !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slick-next:before,
|
||||
.slick-prev:before {
|
||||
color: rgba($primary, 0.2);
|
||||
}
|
||||
|
||||
.slick-next:hover:before,
|
||||
.slick-prev:hover:before {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
/*footer*/
|
||||
footer {
|
||||
ul > li {
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba($white, 0.7);
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.25s ease-out;
|
||||
|
||||
&:hover {
|
||||
color: rgba($white, 0.9);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
/*social icons*/
|
||||
.social {
|
||||
a {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: transparent;
|
||||
display: block;
|
||||
text-align: center;
|
||||
color: gray-100;
|
||||
border-radius: 4px;
|
||||
font-size: 18px;
|
||||
line-height: 45px;
|
||||
|
||||
&:hover {
|
||||
background: $primary;
|
||||
color: $black;
|
||||
transition: all 0.4s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&.social-sm a {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
font-size: 16px;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
&.social-rounded a {
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/*scroll to top */
|
||||
.scroll-top {
|
||||
bottom: 20px;
|
||||
font-size: 20px;
|
||||
height: 40px;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
z-index: 10;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
line-height: 40px;
|
||||
right: -100px;
|
||||
color: $white;
|
||||
background-color: rgba($primary, 0.5);
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($primary, 1);
|
||||
transition: all 0.4s ease-in-out;
|
||||
}
|
||||
|
||||
&.active {
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Icon Boxes */
|
||||
.icon-box {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
background-color: $white;
|
||||
margin: 1rem;
|
||||
|
||||
@each $color, $value in $theme-colors {
|
||||
&.box-#{$color} {
|
||||
color: $value;
|
||||
background-color: rgba($value, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-box-inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
|
||||
&.small {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
&.small-xs {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*all themes colors*/
|
||||
.bg-black {
|
||||
background-color: $black;
|
||||
}
|
||||
.bg-blue {
|
||||
background-color: $blue;
|
||||
}
|
||||
.bg-indigo {
|
||||
background-color: $indigo;
|
||||
}
|
||||
.bg-purple {
|
||||
background-color: $purple;
|
||||
}
|
||||
.bg-pink {
|
||||
background-color: $pink;
|
||||
}
|
||||
.bg-red {
|
||||
background-color: $red;
|
||||
}
|
||||
.bg-orange {
|
||||
background-color: $orange;
|
||||
}
|
||||
.bg-yellow {
|
||||
background-color: $yellow;
|
||||
}
|
||||
.bg-green {
|
||||
background-color: $green;
|
||||
}
|
||||
.bg-teal {
|
||||
background-color: $teal;
|
||||
}
|
||||
.bg-cyan {
|
||||
background-color: $cyan;
|
||||
}
|
||||
|
||||
/*theme switcher*/
|
||||
.switcher-wrap {
|
||||
position: fixed;
|
||||
top: 250px;
|
||||
width: 250px;
|
||||
background: $gray-900;
|
||||
color: $body-color;
|
||||
z-index: 100;
|
||||
padding: 20px;
|
||||
left: -250px;
|
||||
transition: 0.3s;
|
||||
|
||||
&.active {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
a {
|
||||
color: $body-color;
|
||||
|
||||
&:hover {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.color-switcher ul li {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
float: left;
|
||||
margin: 3px;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
|
||||
&.active {
|
||||
border: 3px solid $gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
.switcher-trigger {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: $gray-900;
|
||||
top: 0;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
color: rgba($primary, 0.5);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 0 2px 2px 0;
|
||||
|
||||
&:hover {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.switcher-wrap {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ea-text {
|
||||
text-align: center;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
v="0.4.40"
|
||||
|
||||
curl -L https://github.com/rust-lang/mdBook/releases/download/v$v/mdbook-v$v-x86_64-unknown-linux-gnu.tar.gz -o mdbook.tgz \
|
||||
&& tar xzvf mdbook.tgz \
|
||||
&& ./mdbook build docs/en \
|
||||
&& mkdir dist \
|
||||
&& mv -v docs/en/book/html dist/en \
|
||||
&& mv -v assets dist \
|
||||
&& mv -v docs/landing/index.html docs/landing/css docs/landing/js dist \
|
||||
&& rm -v mdbook \
|
||||
&& rm -v mdbook.tgz
|
@ -0,0 +1,279 @@
|
||||
"""Generate docs from comments."""
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
# Messages --------------------------------------------------------------------
|
||||
|
||||
MESSAGES_DOC_TEMPLATE = """
|
||||
# Full List of Messages
|
||||
|
||||
xplr [messages][1] categorized based on their purpose.
|
||||
|
||||
## Categories
|
||||
|
||||
{categories}
|
||||
|
||||
{msgs}
|
||||
|
||||
## Also See:
|
||||
|
||||
- [Message][1]
|
||||
|
||||
[1]: message.md
|
||||
""".strip()
|
||||
|
||||
CONFIGURATION_DOC_TEMPLATE = """
|
||||
# Configuration
|
||||
|
||||
{doc}
|
||||
|
||||
""".strip()
|
||||
|
||||
|
||||
@dataclass
|
||||
class MsgSection:
|
||||
title: str
|
||||
body: List[str]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MsgCategory:
|
||||
title: str
|
||||
sections: List[MsgSection]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MsgResult:
|
||||
categories: List[MsgCategory]
|
||||
msgs: List[str]
|
||||
|
||||
|
||||
def gen_messages():
|
||||
"""Generate messages.md"""
|
||||
|
||||
path = "./src/msg/in_/external.rs"
|
||||
res = []
|
||||
reading = False
|
||||
|
||||
with open(path) as f:
|
||||
lines = iter(f.read().splitlines())
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
if line.startswith("pub enum ExternalMsg {"):
|
||||
reading = True
|
||||
continue
|
||||
|
||||
if not reading:
|
||||
continue
|
||||
|
||||
if line == "}":
|
||||
break
|
||||
|
||||
if line.startswith("/// ### "):
|
||||
line = line.lstrip("/// ### ").rstrip("-").strip()
|
||||
sec = MsgSection(title=None, body=[])
|
||||
cat = MsgCategory(title=line, sections=[sec])
|
||||
res.append(cat)
|
||||
continue
|
||||
|
||||
if line.startswith("/// "):
|
||||
line = line.lstrip("/// ").strip()
|
||||
res[-1].sections[-1].body.append(line)
|
||||
continue
|
||||
|
||||
if not line or line == "///":
|
||||
res[-1].sections[-1].body.append("")
|
||||
continue
|
||||
|
||||
if line.endswith(","):
|
||||
line = line.split(",")[0].split("(")[0]
|
||||
res[-1].sections[-1].title = line
|
||||
|
||||
sec = MsgSection(title=None, body=[])
|
||||
res[-1].sections.append(sec)
|
||||
continue
|
||||
|
||||
result = MsgResult(categories=[], msgs=[])
|
||||
|
||||
for cat in res:
|
||||
slug = cat.title.lower().replace(" ", "-")
|
||||
result.categories.append(f"- [{cat.title}](#{slug})")
|
||||
result.msgs.append(f"### {cat.title}")
|
||||
result.msgs.append("")
|
||||
|
||||
for sec in cat.sections:
|
||||
if not sec.title:
|
||||
continue
|
||||
|
||||
result.msgs.append(f"#### {sec.title}")
|
||||
result.msgs.append("")
|
||||
for line in sec.body:
|
||||
result.msgs.append(f"{line}")
|
||||
result.msgs.append("")
|
||||
|
||||
messages = MESSAGES_DOC_TEMPLATE.format(
|
||||
categories="\n".join(result.categories), msgs="\n".join(result.msgs)
|
||||
)
|
||||
|
||||
print(messages)
|
||||
with open("./docs/en/src/messages.md", "w") as f:
|
||||
print(messages, file=f)
|
||||
|
||||
|
||||
# Configuration ---------------------------------------------------------------
|
||||
|
||||
|
||||
def gen_configuration():
|
||||
"""Generate the following docs.
|
||||
|
||||
- configuration.md
|
||||
- general-config.md
|
||||
- node_types.md
|
||||
- layouts.md
|
||||
- modes.md
|
||||
- modes.md
|
||||
"""
|
||||
|
||||
path = "./src/init.lua"
|
||||
|
||||
configuration = [[]]
|
||||
general = [[]]
|
||||
node_types = [[]]
|
||||
layouts = [[]]
|
||||
modes = [[]]
|
||||
|
||||
with open(path) as f:
|
||||
lines = iter(f.read().splitlines())
|
||||
|
||||
reading = None
|
||||
|
||||
for line in lines:
|
||||
if line.startswith("---"):
|
||||
continue
|
||||
|
||||
if (
|
||||
line.startswith("-- # Configuration ")
|
||||
or line.startswith("-- ## Config ")
|
||||
or line.startswith("-- ## Function ")
|
||||
or line.startswith("-- ## On Load ")
|
||||
):
|
||||
reading = configuration
|
||||
|
||||
if line.startswith("-- ### General Configuration "):
|
||||
reading = general
|
||||
|
||||
if line.startswith("-- ### Node Types "):
|
||||
reading = node_types
|
||||
|
||||
if line.startswith("-- ### Layouts "):
|
||||
reading = layouts
|
||||
|
||||
if line.startswith("-- ### Modes "):
|
||||
reading = modes
|
||||
|
||||
if not reading:
|
||||
continue
|
||||
|
||||
if line.startswith("-- ") or line == "--":
|
||||
if line.startswith("-- #") and line.endswith("--"):
|
||||
line = "\n{0}\n".format(line.rstrip("-"))
|
||||
|
||||
reading[-1].append(line[3:])
|
||||
continue
|
||||
|
||||
if line.startswith("xplr.") and reading[-1]:
|
||||
reading[-1].insert(0, "\n#### {0}\n".format(line.split()[0]))
|
||||
continue
|
||||
|
||||
if not line.strip() and reading[-1]:
|
||||
reading.append([])
|
||||
continue
|
||||
|
||||
with open("./docs/en/src/configuration.md", "w") as f:
|
||||
doc = "\n".join(["\n".join(c) for c in configuration])
|
||||
print(doc)
|
||||
print(doc, file=f)
|
||||
|
||||
with open("./docs/en/src/general-config.md", "w") as f:
|
||||
doc = "\n".join(["\n".join(c) for c in general])
|
||||
print(doc)
|
||||
print(doc, file=f)
|
||||
|
||||
with open("./docs/en/src/node_types.md", "w") as f:
|
||||
doc = "\n".join(["\n".join(c) for c in node_types])
|
||||
print(doc)
|
||||
print(doc, file=f)
|
||||
|
||||
with open("./docs/en/src/layouts.md", "w") as f:
|
||||
doc = "\n".join(["\n".join(c) for c in layouts])
|
||||
print(doc)
|
||||
print(doc, file=f)
|
||||
|
||||
with open("./docs/en/src/modes.md", "w") as f:
|
||||
doc = "\n".join(["\n".join(c) for c in modes])
|
||||
print(doc)
|
||||
print(doc, file=f)
|
||||
|
||||
|
||||
# xplr.util -------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class Function:
|
||||
doc: List[str]
|
||||
name: str
|
||||
|
||||
|
||||
def gen_xplr_util():
|
||||
|
||||
path = "./src/lua/util.rs"
|
||||
|
||||
functions: List[Function] = []
|
||||
|
||||
with open(path) as f:
|
||||
lines = iter(f.read().splitlines())
|
||||
|
||||
reading = None
|
||||
|
||||
for line in lines:
|
||||
if line.startswith("///"):
|
||||
if reading:
|
||||
reading.doc.append(line[4:])
|
||||
else:
|
||||
reading = Function(doc=[line[4:]], name="")
|
||||
|
||||
if line.startswith("pub fn") and reading:
|
||||
reading.name = "\n### xplr.util." + line.split("<")[0].split()[-1] + "\n"
|
||||
functions.append(reading)
|
||||
reading = None
|
||||
continue
|
||||
|
||||
with open("./docs/en/src/xplr.util.md", "w") as f:
|
||||
for function in functions:
|
||||
print(function.name)
|
||||
print(function.name, file=f)
|
||||
|
||||
print("\n".join(function.doc))
|
||||
print("\n".join(function.doc), file=f)
|
||||
|
||||
if reading:
|
||||
print("\n".join(reading.doc), file=f)
|
||||
|
||||
|
||||
def format_docs():
|
||||
os.system("prettier --write docs/en/src")
|
||||
|
||||
|
||||
def main():
|
||||
gen_messages()
|
||||
gen_configuration()
|
||||
gen_xplr_util()
|
||||
format_docs()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1704262187,
|
||||
"narHash": "sha256-N4j9qghlp/Eb3Z11WF7Cb9U91AXwpascUbLH7YKMcLc=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "65f0d241783c94a08e4c9a3870736fc8854dd520",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
{
|
||||
description = "xplr - A hackable, minimal, fast TUI file explorer";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs";
|
||||
};
|
||||
|
||||
outputs = inputs@{ self, nixpkgs, ... }:
|
||||
let
|
||||
lib = nixpkgs.lib;
|
||||
|
||||
darwin = [ "x86_64-darwin" "aarch64-darwin" ];
|
||||
linux = [ "x86_64-linux" "x86_64-linux-musl" "aarch64-linux" "aarch64-linux-android" "i86_64-linux" ];
|
||||
allSystems = darwin ++ linux;
|
||||
|
||||
forEachSystem = systems: f: lib.genAttrs systems (system: f system);
|
||||
forAllSystems = forEachSystem allSystems;
|
||||
in
|
||||
{
|
||||
packages = forAllSystems (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
rec {
|
||||
# e.g. nix build .#xplr
|
||||
xplr = pkgs.rustPlatform.buildRustPackage rec {
|
||||
name = "xplr";
|
||||
src = ./.;
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
};
|
||||
};
|
||||
|
||||
# e.g. nix build .#cross.x86_64-linux-musl.xplr --impure
|
||||
cross = forEachSystem (lib.filter (sys: sys != system) allSystems) (targetSystem:
|
||||
let
|
||||
crossPkgs = import nixpkgs { localSystem = system; crossSystem = targetSystem; };
|
||||
in
|
||||
{ inherit (crossPkgs) xplr; }
|
||||
);
|
||||
}
|
||||
);
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.xplr);
|
||||
devShells = forAllSystems (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
devRequirements = with pkgs; [
|
||||
gcc
|
||||
gnumake
|
||||
clippy
|
||||
rustc
|
||||
cargo
|
||||
rustfmt
|
||||
rust-analyzer
|
||||
];
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
RUST_BACKTRACE = 1;
|
||||
|
||||
# For cross compilation
|
||||
NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM = 1;
|
||||
|
||||
buildInputs = devRequirements;
|
||||
packages = devRequirements;
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
edition = "2021"
|
||||
max_width = 89
|
||||
tab_spaces = 4
|
||||
use_field_init_shorthand = true
|
@ -0,0 +1,26 @@
|
||||
name: xplr
|
||||
version: git
|
||||
summary: A hackable, minimal, fast TUI file explorer
|
||||
description: |
|
||||
xplr is a terminal UI based file explorer
|
||||
that aims to increase our terminal productivity by being a flexible,
|
||||
interactive orchestrator for the ever growing awesome command-line
|
||||
utilities that work with the file-system.
|
||||
source-code: https://github.com/sayanarijit/xplr
|
||||
issues: https://github.com/sayanarijit/xplr/issues
|
||||
website: https://xplr.dev/
|
||||
|
||||
base: core20
|
||||
grade: devel # must be 'stable' to release into candidate/stable channels
|
||||
confinement: devmode # use 'strict' once you have the right plugs and slots
|
||||
|
||||
|
||||
parts:
|
||||
xplr:
|
||||
plugin: rust
|
||||
source: .
|
||||
|
||||
apps:
|
||||
xplr:
|
||||
command: bin/xplr
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,224 @@
|
||||
// Things of the past, mostly bad decisions, which cannot erased, stays in this
|
||||
// haunted module.
|
||||
|
||||
use crate::app;
|
||||
use crate::lua;
|
||||
use crate::ui::block;
|
||||
use crate::ui::string_to_text;
|
||||
use crate::ui::Constraint;
|
||||
use crate::ui::ContentRendererArg;
|
||||
use crate::ui::UI;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tui::layout::Constraint as TuiConstraint;
|
||||
use tui::layout::Rect as TuiRect;
|
||||
use tui::widgets::Cell;
|
||||
use tui::widgets::List;
|
||||
use tui::widgets::ListItem;
|
||||
use tui::widgets::Paragraph;
|
||||
use tui::widgets::Row;
|
||||
use tui::widgets::Table;
|
||||
use tui::Frame;
|
||||
|
||||
/// A cursed enum from crate::ui.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub enum ContentBody {
|
||||
/// A paragraph to render
|
||||
StaticParagraph { render: String },
|
||||
|
||||
/// A Lua function that returns a paragraph to render
|
||||
DynamicParagraph { render: String },
|
||||
|
||||
/// List to render
|
||||
StaticList { render: Vec<String> },
|
||||
|
||||
/// A Lua function that returns lines to render
|
||||
DynamicList { render: String },
|
||||
|
||||
/// A table to render
|
||||
StaticTable {
|
||||
widths: Vec<Constraint>,
|
||||
col_spacing: Option<u16>,
|
||||
render: Vec<Vec<String>>,
|
||||
},
|
||||
|
||||
/// A Lua function that returns a table to render
|
||||
DynamicTable {
|
||||
widths: Vec<Constraint>,
|
||||
col_spacing: Option<u16>,
|
||||
render: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// A cursed struct from crate::ui.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct CustomContent {
|
||||
pub title: Option<String>,
|
||||
pub body: ContentBody,
|
||||
}
|
||||
|
||||
/// A cursed function from crate::ui.
|
||||
pub fn draw_custom_content(
|
||||
ui: &mut UI,
|
||||
f: &mut Frame,
|
||||
layout_size: TuiRect,
|
||||
app: &app::App,
|
||||
content: CustomContent,
|
||||
) {
|
||||
let config = app.config.general.panel_ui.default.clone();
|
||||
let title = content.title;
|
||||
let body = content.body;
|
||||
|
||||
match body {
|
||||
ContentBody::StaticParagraph { render } => {
|
||||
let render = string_to_text(render);
|
||||
let content = Paragraph::new(render).block(block(
|
||||
config,
|
||||
title.map(|t| format!(" {t} ")).unwrap_or_default(),
|
||||
));
|
||||
f.render_widget(content, layout_size);
|
||||
}
|
||||
|
||||
ContentBody::DynamicParagraph { render } => {
|
||||
let ctx = ContentRendererArg {
|
||||
app: app.to_lua_ctx_light(),
|
||||
layout_size: layout_size.into(),
|
||||
screen_size: ui.screen_size.into(),
|
||||
scrolltop: ui.scrolltop as u16,
|
||||
};
|
||||
|
||||
let render = lua::serialize(ui.lua, &ctx)
|
||||
.map(|arg| {
|
||||
lua::call(ui.lua, &render, arg).unwrap_or_else(|e| format!("{e:?}"))
|
||||
})
|
||||
.unwrap_or_else(|e| e.to_string());
|
||||
|
||||
let render = string_to_text(render);
|
||||
|
||||
let content = Paragraph::new(render).block(block(
|
||||
config,
|
||||
title.map(|t| format!(" {t} ")).unwrap_or_default(),
|
||||
));
|
||||
f.render_widget(content, layout_size);
|
||||
}
|
||||
|
||||
ContentBody::StaticList { render } => {
|
||||
let items = render
|
||||
.into_iter()
|
||||
.map(string_to_text)
|
||||
.map(ListItem::new)
|
||||
.collect::<Vec<ListItem>>();
|
||||
|
||||
let content = List::new(items).block(block(
|
||||
config,
|
||||
title.map(|t| format!(" {t} ")).unwrap_or_default(),
|
||||
));
|
||||
f.render_widget(content, layout_size);
|
||||
}
|
||||
|
||||
ContentBody::DynamicList { render } => {
|
||||
let ctx = ContentRendererArg {
|
||||
app: app.to_lua_ctx_light(),
|
||||
layout_size: layout_size.into(),
|
||||
screen_size: ui.screen_size.into(),
|
||||
scrolltop: ui.scrolltop as u16,
|
||||
};
|
||||
|
||||
let items = lua::serialize(ui.lua, &ctx)
|
||||
.map(|arg| {
|
||||
lua::call(ui.lua, &render, arg)
|
||||
.unwrap_or_else(|e| vec![format!("{e:?}")])
|
||||
})
|
||||
.unwrap_or_else(|e| vec![e.to_string()])
|
||||
.into_iter()
|
||||
.map(string_to_text)
|
||||
.map(ListItem::new)
|
||||
.collect::<Vec<ListItem>>();
|
||||
|
||||
let content = List::new(items).block(block(
|
||||
config,
|
||||
title.map(|t| format!(" {t} ")).unwrap_or_default(),
|
||||
));
|
||||
f.render_widget(content, layout_size);
|
||||
}
|
||||
|
||||
ContentBody::StaticTable {
|
||||
widths,
|
||||
col_spacing,
|
||||
render,
|
||||
} => {
|
||||
let rows = render
|
||||
.into_iter()
|
||||
.map(|cols| {
|
||||
Row::new(
|
||||
cols.into_iter()
|
||||
.map(string_to_text)
|
||||
.map(Cell::from)
|
||||
.collect::<Vec<Cell>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Row>>();
|
||||
|
||||
let widths = widths
|
||||
.into_iter()
|
||||
.map(|w| w.to_tui(ui.screen_size, layout_size))
|
||||
.collect::<Vec<TuiConstraint>>();
|
||||
|
||||
let content = Table::new(rows, widths)
|
||||
.column_spacing(col_spacing.unwrap_or(1))
|
||||
.block(block(
|
||||
config,
|
||||
title.map(|t| format!(" {t} ")).unwrap_or_default(),
|
||||
));
|
||||
|
||||
f.render_widget(content, layout_size);
|
||||
}
|
||||
|
||||
ContentBody::DynamicTable {
|
||||
widths,
|
||||
col_spacing,
|
||||
render,
|
||||
} => {
|
||||
let ctx = ContentRendererArg {
|
||||
app: app.to_lua_ctx_light(),
|
||||
layout_size: layout_size.into(),
|
||||
screen_size: ui.screen_size.into(),
|
||||
scrolltop: ui.scrolltop as u16,
|
||||
};
|
||||
|
||||
let rows = lua::serialize(ui.lua, &ctx)
|
||||
.map(|arg| {
|
||||
lua::call(ui.lua, &render, arg)
|
||||
.unwrap_or_else(|e| vec![vec![format!("{e:?}")]])
|
||||
})
|
||||
.unwrap_or_else(|e| vec![vec![e.to_string()]])
|
||||
.into_iter()
|
||||
.map(|cols| {
|
||||
Row::new(
|
||||
cols.into_iter()
|
||||
.map(string_to_text)
|
||||
.map(Cell::from)
|
||||
.collect::<Vec<Cell>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<Row>>();
|
||||
|
||||
let widths = widths
|
||||
.into_iter()
|
||||
.map(|w| w.to_tui(ui.screen_size, layout_size))
|
||||
.collect::<Vec<TuiConstraint>>();
|
||||
|
||||
let mut content = Table::new(rows, &widths).block(block(
|
||||
config,
|
||||
title.map(|t| format!(" {t} ")).unwrap_or_default(),
|
||||
));
|
||||
|
||||
if let Some(col_spacing) = col_spacing {
|
||||
content = content.column_spacing(col_spacing);
|
||||
};
|
||||
|
||||
f.render_widget(content, layout_size);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@
|
||||
use crate::node::Node;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct DirectoryBuffer {
|
||||
pub parent: String,
|
||||
pub nodes: Vec<Node>,
|
||||
pub total: usize,
|
||||
pub focus: usize,
|
||||
|
||||
#[serde(skip, default = "now")]
|
||||
pub explored_at: OffsetDateTime,
|
||||
}
|
||||
|
||||
impl DirectoryBuffer {
|
||||
pub fn new(parent: String, nodes: Vec<Node>, focus: usize) -> Self {
|
||||
let total = nodes.len();
|
||||
Self {
|
||||
parent,
|
||||
nodes,
|
||||
total,
|
||||
focus,
|
||||
explored_at: now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn focused_node(&self) -> Option<&Node> {
|
||||
self.nodes.get(self.focus)
|
||||
}
|
||||
}
|
||||
|
||||
fn now() -> OffsetDateTime {
|
||||
OffsetDateTime::now_local()
|
||||
.ok()
|
||||
.unwrap_or_else(OffsetDateTime::now_utc)
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref BASE_DIRS: Option<BaseDirectories> = BaseDirectories::new().ok();
|
||||
}
|
||||
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
home::home_dir()
|
||||
}
|
||||
|
||||
pub fn config_dir() -> Option<PathBuf> {
|
||||
BASE_DIRS.as_ref().map(|base| base.get_config_home())
|
||||
}
|
||||
|
||||
pub fn runtime_dir() -> PathBuf {
|
||||
let Some(dir) = BASE_DIRS
|
||||
.as_ref()
|
||||
.and_then(|base| base.get_runtime_directory().ok())
|
||||
else {
|
||||
return env::temp_dir();
|
||||
};
|
||||
dir.clone()
|
||||
}
|
@ -1,77 +1,106 @@
|
||||
use crate::app::Task;
|
||||
use crate::app::{ExternalMsg, InternalMsg, MsgIn};
|
||||
use crate::input::Key;
|
||||
use anyhow::Result;
|
||||
use anyhow::Error;
|
||||
use crossterm::event::{self, Event, MouseEventKind};
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn pause_reading(tx_event_reader: &Sender<bool>, rx_loop_waiter: &Receiver<()>) -> Result<()> {
|
||||
tx_event_reader.send(true)?;
|
||||
rx_loop_waiter.recv()?;
|
||||
Ok(())
|
||||
pub(crate) struct EventReader {
|
||||
task_sender: Sender<Task>,
|
||||
stopper: Option<(Sender<bool>, Receiver<()>)>,
|
||||
}
|
||||
|
||||
pub fn resume_reading(tx_event_reader: &Sender<bool>, rx_loop_waiter: &Receiver<()>) -> Result<()> {
|
||||
tx_event_reader.send(false)?;
|
||||
rx_loop_waiter.recv()?;
|
||||
Ok(())
|
||||
impl EventReader {
|
||||
pub(crate) fn new(task_sender: Sender<Task>) -> Self {
|
||||
Self {
|
||||
task_sender,
|
||||
stopper: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start(&mut self) {
|
||||
let sender = self.task_sender.clone();
|
||||
let (tx_stopper, rx_stopper) = mpsc::channel();
|
||||
let (tx_ack, rx_ack) = mpsc::channel();
|
||||
self.stopper = Some((tx_stopper, rx_ack));
|
||||
|
||||
thread::spawn(move || {
|
||||
keep_reading(sender, rx_stopper, tx_ack);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn stop(&self) {
|
||||
if let Some((stopper, ack)) = &self.stopper {
|
||||
stopper.send(true).unwrap_or_default(); // Let's not panic when xplr stops.
|
||||
ack.recv().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keep_reading(
|
||||
fn keep_reading(
|
||||
tx_msg_in: Sender<Task>,
|
||||
rx_event_reader: Receiver<bool>,
|
||||
tx_loop_waiter: Sender<()>,
|
||||
rx_stopper: Receiver<bool>,
|
||||
tx_ack: Sender<()>,
|
||||
) {
|
||||
thread::spawn(move || {
|
||||
let mut is_paused = false;
|
||||
loop {
|
||||
if let Ok(paused) = rx_event_reader.try_recv() {
|
||||
is_paused = paused;
|
||||
tx_loop_waiter.send(()).unwrap();
|
||||
};
|
||||
loop {
|
||||
if rx_stopper.try_recv().unwrap_or(false) {
|
||||
tx_ack.send(()).unwrap();
|
||||
break;
|
||||
} else if event::poll(std::time::Duration::from_millis(150)).unwrap_or_default()
|
||||
{
|
||||
// NOTE: The poll timeout need to stay low, else spawning sub subshell
|
||||
// and start typing immediately will cause panic.
|
||||
// To reproduce, press `:`, then press and hold `!`.
|
||||
let res = match event::read() {
|
||||
Ok(Event::Key(key)) => {
|
||||
let key = Key::from_event(key);
|
||||
let msg = MsgIn::Internal(InternalMsg::HandleKey(key));
|
||||
tx_msg_in
|
||||
.send(Task::new(msg, Some(key)))
|
||||
.map_err(Error::new)
|
||||
}
|
||||
|
||||
if is_paused {
|
||||
thread::sleep(std::time::Duration::from_millis(200));
|
||||
} else if event::poll(std::time::Duration::from_millis(150)).unwrap_or_default() {
|
||||
// NOTE: The poll timeout need to stay low, else spawning sub subshell
|
||||
// and start typing immediately will cause panic.
|
||||
// To reproduce, press `:`, then press and hold `!`.
|
||||
match event::read() {
|
||||
Ok(Event::Key(key)) => {
|
||||
let key = Key::from_event(key);
|
||||
let msg = MsgIn::Internal(InternalMsg::HandleKey(key));
|
||||
tx_msg_in.send(Task::new(msg, Some(key))).unwrap();
|
||||
Ok(Event::Mouse(evt)) => match evt.kind {
|
||||
MouseEventKind::ScrollUp => {
|
||||
let msg = MsgIn::External(ExternalMsg::FocusPrevious);
|
||||
tx_msg_in.send(Task::new(msg, None)).map_err(Error::new)
|
||||
}
|
||||
|
||||
Ok(Event::Mouse(evt)) => match evt.kind {
|
||||
MouseEventKind::ScrollUp => {
|
||||
let msg = MsgIn::External(ExternalMsg::FocusPrevious);
|
||||
tx_msg_in.send(Task::new(msg, None)).unwrap();
|
||||
}
|
||||
MouseEventKind::ScrollDown => {
|
||||
let msg = MsgIn::External(ExternalMsg::FocusNext);
|
||||
tx_msg_in.send(Task::new(msg, None)).map_err(Error::new)
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
|
||||
MouseEventKind::ScrollDown => {
|
||||
let msg = MsgIn::External(ExternalMsg::FocusNext);
|
||||
tx_msg_in.send(Task::new(msg, None)).unwrap();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Ok(Event::Resize(_, _)) => {
|
||||
let msg = MsgIn::External(ExternalMsg::Refresh);
|
||||
tx_msg_in.send(Task::new(msg, None)).map_err(Error::new)
|
||||
}
|
||||
|
||||
Ok(Event::Resize(_, _)) => {
|
||||
let msg = MsgIn::External(ExternalMsg::Refresh);
|
||||
tx_msg_in.send(Task::new(msg, None)).unwrap();
|
||||
}
|
||||
Ok(Event::FocusGained) => Ok(()),
|
||||
Ok(Event::FocusLost) => Ok(()),
|
||||
|
||||
Err(e) => {
|
||||
tx_msg_in
|
||||
.send(Task::new(
|
||||
MsgIn::External(ExternalMsg::LogError(e.to_string())),
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
Ok(Event::Paste(text)) => {
|
||||
let msg = MsgIn::External(ExternalMsg::BufferInput(text));
|
||||
tx_msg_in.send(Task::new(msg, None)).map_err(Error::new)
|
||||
}
|
||||
|
||||
Err(e) => Err(Error::new(e)),
|
||||
};
|
||||
|
||||
if let Err(e) = res {
|
||||
tx_msg_in
|
||||
.send(Task::new(
|
||||
MsgIn::External(ExternalMsg::LogError(e.to_string())),
|
||||
None,
|
||||
))
|
||||
.unwrap_or_default(); // Let's not panic when xplr stops
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,904 @@
|
||||
use crate::app::VERSION;
|
||||
use crate::config::NodeTypesConfig;
|
||||
use crate::explorer;
|
||||
use crate::lua;
|
||||
use crate::msg::in_::external::ExplorerConfig;
|
||||
use crate::node::Node;
|
||||
use crate::path;
|
||||
use crate::path::RelativityConfig;
|
||||
use crate::permissions::Octal;
|
||||
use crate::permissions::Permissions;
|
||||
use crate::ui;
|
||||
use crate::ui::Layout;
|
||||
use crate::ui::Style;
|
||||
use crate::ui::WrapOptions;
|
||||
use anyhow::Result;
|
||||
use lazy_static::lazy_static;
|
||||
use lscolors::LsColors;
|
||||
use mlua::Error as LuaError;
|
||||
use mlua::Lua;
|
||||
use mlua::LuaSerdeExt;
|
||||
use mlua::Table;
|
||||
use mlua::Value;
|
||||
use path_absolutize::*;
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json as json;
|
||||
use serde_yaml as yaml;
|
||||
use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
lazy_static! {
|
||||
static ref LS_COLORS: LsColors = LsColors::from_env().unwrap_or_default();
|
||||
}
|
||||
|
||||
/// Get the xplr version details.
|
||||
///
|
||||
/// Type: function() -> { major: number, minor: number, patch: number }
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.version()
|
||||
/// -- { major = 0, minor = 0, patch = 0 }
|
||||
/// ```
|
||||
pub fn version<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
struct Version {
|
||||
major: u16,
|
||||
minor: u16,
|
||||
patch: u16,
|
||||
}
|
||||
|
||||
let func = lua.create_function(|lua, ()| {
|
||||
let (major, minor, patch, _) =
|
||||
lua::parse_version(VERSION).map_err(LuaError::custom)?;
|
||||
|
||||
let version = Version {
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
};
|
||||
|
||||
let res = lua::serialize(lua, &version).map_err(LuaError::custom)?;
|
||||
Ok(res)
|
||||
})?;
|
||||
|
||||
util.set("version", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Print the given value to the console, and return it as a string.
|
||||
/// Useful for debugging.
|
||||
///
|
||||
/// Type: function( value ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.debug({ foo = "bar", bar = function() end })
|
||||
/// -- {
|
||||
/// -- ["bar"] = function: 0x55e5cebdeae0,
|
||||
/// -- ["foo"] = "bar",
|
||||
/// -- }
|
||||
/// ```
|
||||
pub fn debug<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|_, value: Value| {
|
||||
let log = format!("{:#?}", value);
|
||||
println!("{}", log);
|
||||
Ok(log)
|
||||
})?;
|
||||
util.set("debug", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Clone/deepcopy a Lua value. Doesn't work with functions.
|
||||
///
|
||||
/// Type: function( value ) -> value
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// local val = { foo = "bar" }
|
||||
/// local val_clone = xplr.util.clone(val)
|
||||
/// val.foo = "baz"
|
||||
/// print(val_clone.foo)
|
||||
/// -- "bar"
|
||||
/// ```
|
||||
pub fn clone<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(move |lua, value: Value| {
|
||||
lua::serialize(lua, &value).map_err(LuaError::custom)
|
||||
})?;
|
||||
util.set("clone", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Check if the given path exists.
|
||||
///
|
||||
/// Type: function( path:string ) -> boolean
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.exists("/foo/bar")
|
||||
/// -- true
|
||||
/// ```
|
||||
pub fn exists<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(move |_, path: String| Ok(PathBuf::from(path).exists()))?;
|
||||
util.set("exists", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Check if the given path is a directory.
|
||||
///
|
||||
/// Type: function( path:string ) -> boolean
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.is_dir("/foo/bar")
|
||||
/// -- true
|
||||
/// ```
|
||||
pub fn is_dir<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(move |_, path: String| Ok(PathBuf::from(path).is_dir()))?;
|
||||
util.set("is_dir", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Check if the given path is a file.
|
||||
///
|
||||
/// Type: function( path:string ) -> boolean
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.is_file("/foo/bar")
|
||||
/// -- true
|
||||
/// ```
|
||||
pub fn is_file<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(move |_, path: String| Ok(PathBuf::from(path).is_file()))?;
|
||||
util.set("is_file", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Check if the given path is a symlink.
|
||||
///
|
||||
/// Type: function( path:string ) -> boolean
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.is_file("/foo/bar")
|
||||
/// -- true
|
||||
/// ```
|
||||
pub fn is_symlink<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua
|
||||
.create_function(move |_, path: String| Ok(PathBuf::from(path).is_symlink()))?;
|
||||
util.set("is_symlink", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Check if the given path is an absolute path.
|
||||
///
|
||||
/// Type: function( path:string ) -> boolean
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.is_absolute("/foo/bar")
|
||||
/// -- true
|
||||
/// ```
|
||||
pub fn is_absolute<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua
|
||||
.create_function(move |_, path: String| Ok(PathBuf::from(path).is_absolute()))?;
|
||||
util.set("is_absolute", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Split a path into its components.
|
||||
///
|
||||
/// Type: function( path:string ) -> boolean
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.path_split("/foo/bar")
|
||||
/// -- { "/", "foo", "bar" }
|
||||
///
|
||||
/// xplr.util.path_split(".././foo")
|
||||
/// -- { "..", "foo" }
|
||||
/// ```
|
||||
pub fn path_split<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(move |_, path: String| {
|
||||
let components: Vec<String> = PathBuf::from(path)
|
||||
.components()
|
||||
.map(|c| c.as_os_str().to_string_lossy().to_string())
|
||||
.collect();
|
||||
Ok(components)
|
||||
})?;
|
||||
util.set("path_split", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get [Node][5] information of a given path.
|
||||
/// Doesn't check if the path exists.
|
||||
/// Returns nil if the path is "/".
|
||||
/// Errors out if absolute path can't be obtained.
|
||||
///
|
||||
/// Type: function( path:string ) -> [Node][5]|nil
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.node("./bar")
|
||||
/// -- { parent = "/pwd", relative_path = "bar", absolute_path = "/pwd/bar", ... }
|
||||
///
|
||||
/// xplr.util.node("/")
|
||||
/// -- nil
|
||||
/// ```
|
||||
pub fn node<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(move |lua, path: String| {
|
||||
let path = PathBuf::from(path);
|
||||
let abs = path.absolutize()?;
|
||||
match (abs.parent(), abs.file_name()) {
|
||||
(Some(parent), Some(name)) => {
|
||||
let node = Node::new(
|
||||
parent.to_string_lossy().to_string(),
|
||||
name.to_string_lossy().to_string(),
|
||||
);
|
||||
Ok(lua::serialize(lua, &node).map_err(LuaError::custom)?)
|
||||
}
|
||||
(_, _) => Ok(Value::Nil),
|
||||
}
|
||||
})?;
|
||||
util.set("node", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the configured [Node Type][6] of a given [Node][5].
|
||||
///
|
||||
/// Type: function( [Node][5], [xplr.config.node_types][7]|nil ) -> [Node Type][6]
|
||||
///
|
||||
/// If the second argument is missing, global config `xplr.config.node_types`
|
||||
/// will be used.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.node_type(app.focused_node)
|
||||
/// -- { style = { fg = "Red", ... }, meta = { icon = "", ... } ... }
|
||||
///
|
||||
/// xplr.util.node_type(xplr.util.node("/foo/bar"), xplr.config.node_types)
|
||||
/// -- { style = { fg = "Red", ... }, meta = { icon = "", ... } ... }
|
||||
/// ```
|
||||
pub fn node_type<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(move |lua, (node, config): (Table, Option<Table>)| {
|
||||
let node: Node = lua.from_value(Value::Table(node))?;
|
||||
let config: Table = if let Some(config) = config {
|
||||
config
|
||||
} else {
|
||||
lua.globals()
|
||||
.get::<_, Table>("xplr")?
|
||||
.get::<_, Table>("config")?
|
||||
.get::<_, Table>("node_types")?
|
||||
};
|
||||
let config: NodeTypesConfig = lua.from_value(Value::Table(config))?;
|
||||
let node_type = config.get(&node);
|
||||
let node_type = lua::serialize(lua, &node_type).map_err(LuaError::custom)?;
|
||||
Ok(node_type)
|
||||
})?;
|
||||
util.set("node_type", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the directory name of a given path.
|
||||
///
|
||||
/// Type: function( path:string ) -> path:string|nil
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.dirname("/foo/bar")
|
||||
/// -- "/foo"
|
||||
/// ```
|
||||
pub fn dirname<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|_, path: String| {
|
||||
let parent = PathBuf::from(path)
|
||||
.parent()
|
||||
.map(|p| p.to_string_lossy().to_string());
|
||||
Ok(parent)
|
||||
})?;
|
||||
util.set("dirname", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the base name of a given path.
|
||||
///
|
||||
/// Type: function( path:string ) -> path:string|nil
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.basename("/foo/bar")
|
||||
/// -- "bar"
|
||||
/// ```
|
||||
pub fn basename<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|_, path: String| {
|
||||
let parent = PathBuf::from(path)
|
||||
.file_name()
|
||||
.map(|p| p.to_string_lossy().to_string());
|
||||
Ok(parent)
|
||||
})?;
|
||||
util.set("basename", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the absolute path of the given path by prepending $PWD.
|
||||
/// It doesn't check if the path exists.
|
||||
///
|
||||
/// Type: function( path:string ) -> path:string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.absolute("foo/bar")
|
||||
/// -- "/tmp/foo/bar"
|
||||
/// ```
|
||||
pub fn absolute<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|_, path: String| {
|
||||
let abs = PathBuf::from(path)
|
||||
.absolutize()?
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
Ok(abs)
|
||||
})?;
|
||||
util.set("absolute", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the relative path based on the given base path or current working dir.
|
||||
/// Will error if it fails to determine a relative path.
|
||||
///
|
||||
/// Type: function( path:string, options:table|nil ) -> path:string
|
||||
///
|
||||
/// Options type: { base:string|nil, with_prefix_dots:bookean|nil, without_suffix_dots:boolean|nil }
|
||||
///
|
||||
/// - If `base` path is given, the path will be relative to it.
|
||||
/// - If `with_prefix_dots` is true, the path will always start with dots `..` / `.`
|
||||
/// - If `without_suffix_dots` is true, the name will be visible instead of dots `..` / `.`
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.relative_to("/present/working/directory")
|
||||
/// -- "."
|
||||
///
|
||||
/// xplr.util.relative_to("/present/working/directory/foo")
|
||||
/// -- "foo"
|
||||
///
|
||||
/// xplr.util.relative_to("/present/working/directory/foo", { with_prefix_dots = true })
|
||||
/// -- "./foo"
|
||||
///
|
||||
/// xplr.util.relative_to("/present/working/directory", { without_suffix_dots = true })
|
||||
/// -- "../directory"
|
||||
///
|
||||
/// xplr.util.relative_to("/present/working")
|
||||
/// -- ".."
|
||||
///
|
||||
/// xplr.util.relative_to("/present/working", { without_suffix_dots = true })
|
||||
/// -- "../../working"
|
||||
///
|
||||
/// xplr.util.relative_to("/present/working/directory", { base = "/present/foo/bar" })
|
||||
/// -- "../../working/directory"
|
||||
/// ```
|
||||
pub fn relative_to<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, (path, config): (String, Option<Table>)| {
|
||||
let config: Option<RelativityConfig<String>> =
|
||||
lua.from_value(config.map(Value::Table).unwrap_or(Value::Nil))?;
|
||||
path::relative_to(path, config.as_ref())
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.map_err(LuaError::custom)
|
||||
})?;
|
||||
util.set("relative_to", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Shorten the given absolute path using the following rules:
|
||||
/// - either relative to your home dir if it makes sense
|
||||
/// - or relative to the current working directory
|
||||
/// - or absolute path if it makes the most sense
|
||||
///
|
||||
/// Type: Similar to `xplr.util.relative_to`
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.shorten("/home/username/.config")
|
||||
/// -- "~/.config"
|
||||
///
|
||||
/// xplr.util.shorten("/present/working/directory")
|
||||
/// -- "."
|
||||
///
|
||||
/// xplr.util.shorten("/present/working/directory/foo")
|
||||
/// -- "foo"
|
||||
///
|
||||
/// xplr.util.shorten("/present/working/directory/foo", { with_prefix_dots = true })
|
||||
/// -- "./foo"
|
||||
///
|
||||
/// xplr.util.shorten("/present/working/directory", { without_suffix_dots = true })
|
||||
/// -- "../directory"
|
||||
///
|
||||
/// xplr.util.shorten("/present/working/directory", { base = "/present/foo/bar" })
|
||||
/// -- "../../working/directory"
|
||||
///
|
||||
/// xplr.util.shorten("/tmp")
|
||||
/// -- "/tmp"
|
||||
/// ```
|
||||
pub fn shorten<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(move |lua, (path, config): (String, Option<Table>)| {
|
||||
let config: Option<RelativityConfig<String>> =
|
||||
lua.from_value(config.map(Value::Table).unwrap_or(Value::Nil))?;
|
||||
path::shorten(path, config.as_ref()).map_err(LuaError::custom)
|
||||
})?;
|
||||
util.set("shorten", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Explore directories with the given explorer config.
|
||||
///
|
||||
/// Type: function( path:string, [ExplorerConfig][1]|nil ) -> { [Node][2], ... }
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
///
|
||||
/// xplr.util.explore("/tmp")
|
||||
/// -- { { absolute_path = "/tmp/a", ... }, ... }
|
||||
///
|
||||
/// xplr.util.explore("/tmp", app.explorer_config)
|
||||
/// -- { { absolute_path = "/tmp/a", ... }, ... }
|
||||
/// ```
|
||||
pub fn explore<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, (path, config): (String, Option<Table>)| {
|
||||
let config: ExplorerConfig = if let Some(cfg) = config {
|
||||
lua.from_value(Value::Table(cfg))?
|
||||
} else {
|
||||
ExplorerConfig::default()
|
||||
};
|
||||
|
||||
let nodes = explorer::explore(&PathBuf::from(path), &config)
|
||||
.map_err(LuaError::custom)?;
|
||||
let res = lua::serialize(lua, &nodes).map_err(LuaError::custom)?;
|
||||
Ok(res)
|
||||
})?;
|
||||
util.set("explore", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Execute shell commands safely.
|
||||
///
|
||||
/// Type: function( program:string, args:{ string, ... }|nil ) -> { stdout = string, stderr = string, returncode = number|nil }
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.shell_execute("pwd")
|
||||
/// -- { stdout = "/present/working/directory", stderr = "", returncode = 0 }
|
||||
///
|
||||
/// xplr.util.shell_execute("bash", {"-c", "xplr --help"})
|
||||
/// -- { stdout = "xplr...", stderr = "", returncode = 0 }
|
||||
/// ```
|
||||
pub fn shell_execute<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(|lua, (program, args): (String, Option<Vec<String>>)| {
|
||||
let mut cmd = Command::new(program);
|
||||
let mut cmd_ref = &mut cmd;
|
||||
if let Some(args) = args {
|
||||
cmd_ref = cmd_ref.args(args)
|
||||
};
|
||||
let output = cmd_ref.output()?;
|
||||
|
||||
let res = lua.create_table()?;
|
||||
res.set("stdout", String::from_utf8_lossy(&output.stdout))?;
|
||||
res.set("stderr", String::from_utf8_lossy(&output.stderr))?;
|
||||
res.set("returncode", output.status.code())?;
|
||||
Ok(res)
|
||||
})?;
|
||||
util.set("shell_execute", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Quote commands and paths safely.
|
||||
///
|
||||
/// Type: function( string ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.shell_quote("a'b\"c")
|
||||
/// -- 'a'"'"'b"c'
|
||||
/// ```
|
||||
pub fn shell_quote<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|_, string: String| {
|
||||
Ok(format!("'{}'", string.replace('\'', r#"'"'"'"#)))
|
||||
})?;
|
||||
util.set("shell_quote", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Escape commands and paths safely.
|
||||
///
|
||||
/// Type: function( string ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.shell_escape("a'b\"c")
|
||||
/// -- "\"a'b\\\"c\""
|
||||
/// ```
|
||||
pub fn shell_escape<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(move |_, string: String| {
|
||||
let val = path::escape(&string).to_string();
|
||||
Ok(val)
|
||||
})?;
|
||||
util.set("shell_escape", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Load JSON string into Lua value.
|
||||
///
|
||||
/// Type: function( string ) -> any
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.from_json([[{"foo": "bar"}]])
|
||||
/// -- { foo = "bar" }
|
||||
/// ```
|
||||
pub fn from_json<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, string: String| {
|
||||
let val = json::from_str::<yaml::Value>(&string).map_err(LuaError::custom)?;
|
||||
lua::serialize(lua, &val).map_err(Error::custom)
|
||||
})?;
|
||||
util.set("from_json", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Dump Lua value into JSON (i.e. also YAML) string.
|
||||
///
|
||||
/// Type: function( value ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.to_json({ foo = "bar" })
|
||||
/// -- [[{ "foo": "bar" }]]
|
||||
///
|
||||
/// xplr.util.to_json({ foo = "bar" }, { pretty = true })
|
||||
/// -- [[{
|
||||
/// -- "foo": "bar"
|
||||
/// -- }]]
|
||||
/// ```
|
||||
pub fn to_json<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
struct Options {
|
||||
pretty: bool,
|
||||
}
|
||||
|
||||
let func =
|
||||
lua.create_function(|lua, (value, options): (Value, Option<Table>)| {
|
||||
let options: Options = if let Some(o) = options {
|
||||
lua.from_value(Value::Table(o))?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
if options.pretty {
|
||||
json::to_string_pretty(&value).map_err(Error::custom)
|
||||
} else {
|
||||
json::to_string(&value).map_err(Error::custom)
|
||||
}
|
||||
})?;
|
||||
util.set("to_json", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Load YAML (i.e. also JSON) string into Lua value.
|
||||
///
|
||||
/// Type: function( string ) -> value
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.from_yaml([[{foo: bar}]])
|
||||
/// -- { foo = "bar" }
|
||||
/// ```
|
||||
pub fn from_yaml<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, string: String| {
|
||||
let val = yaml::from_str::<yaml::Value>(&string).map_err(LuaError::custom)?;
|
||||
lua::serialize(lua, &val).map_err(Error::custom)
|
||||
})?;
|
||||
util.set("from_yaml", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Dump Lua value into YAML string.
|
||||
///
|
||||
/// Type: function( value ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.to_yaml({ foo = "bar" })
|
||||
/// -- "foo: bar"
|
||||
/// ```
|
||||
pub fn to_yaml<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
pub struct Options {
|
||||
pretty: bool,
|
||||
}
|
||||
|
||||
let func = lua.create_function(|_, value: Value| {
|
||||
yaml::to_string(&value).map_err(Error::custom)
|
||||
})?;
|
||||
util.set("to_yaml", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get a [Style][3] object for the given path based on the LS_COLORS
|
||||
/// environment variable.
|
||||
///
|
||||
/// Type: function( path:string ) -> [Style][3]
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.lscolor("Desktop")
|
||||
/// -- { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} }
|
||||
/// ```
|
||||
pub fn lscolor<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(move |lua, path: String| {
|
||||
let style = LS_COLORS
|
||||
.style_for_path(path)
|
||||
.map(Style::from)
|
||||
.unwrap_or_default();
|
||||
lua::serialize(lua, &style).map_err(LuaError::custom)
|
||||
})?;
|
||||
util.set("lscolor", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Apply style (escape sequence) to string using a given [Style][3] object.
|
||||
///
|
||||
/// Type: function( string, [Style][3]|nil ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.paint("Desktop", { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} })
|
||||
/// -- "\u001b[31mDesktop\u001b[0m"
|
||||
/// ```
|
||||
pub fn paint<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(|lua, (string, style): (String, Option<Table>)| {
|
||||
if *ui::NO_COLOR {
|
||||
return Ok(string);
|
||||
}
|
||||
|
||||
if let Some(style) = style {
|
||||
let style: Style = lua.from_value(Value::Table(style))?;
|
||||
let ansi_style: nu_ansi_term::Style = style.into();
|
||||
Ok::<String, LuaError>(ansi_style.paint(string).to_string())
|
||||
} else {
|
||||
Ok(string)
|
||||
}
|
||||
})?;
|
||||
util.set("paint", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Mix multiple [Style][3] objects into one.
|
||||
///
|
||||
/// Type: function( { [Style][3], [Style][3], ... } ) -> [Style][3]
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.style_mix({{ fg = "Red" }, { bg = "Blue" }, { add_modifiers = {"Bold"} }})
|
||||
/// -- { fg = "Red", bg = "Blue", add_modifiers = { "Bold" }, sub_modifiers = {} }
|
||||
/// ```
|
||||
pub fn style_mix<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, styles: Vec<Table>| {
|
||||
let mut style = Style::default();
|
||||
for other in styles {
|
||||
let other: Style = lua.from_value(Value::Table(other))?;
|
||||
style = style.extend(&other);
|
||||
}
|
||||
|
||||
lua::serialize(lua, &style).map_err(LuaError::custom)
|
||||
})?;
|
||||
util.set("style_mix", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Wrap the given text to fit the specified width.
|
||||
/// It will try to not split words when possible.
|
||||
///
|
||||
/// Type: function( string, options:number|table ) -> { string, ...}
|
||||
///
|
||||
/// Options type: { width = number, initial_indent = string|nil, subsequent_indent = string|nil, break_words = boolean|nil }
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.textwrap("this will be cut off", 11)
|
||||
/// -- { "this will', 'be cut off" }
|
||||
///
|
||||
/// xplr.util.textwrap(
|
||||
/// "this will be cut off",
|
||||
/// { width = 12, initial_indent = "", subsequent_indent = " ", break_words = false }
|
||||
/// )
|
||||
/// -- { "this will be", " cut off" }
|
||||
/// ```
|
||||
pub fn textwrap<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, (text, options): (String, Value)| {
|
||||
let lines = match lua.from_value::<usize>(options.clone()) {
|
||||
Ok(width) => textwrap::wrap(&text, width),
|
||||
Err(_) => {
|
||||
let options = lua.from_value::<WrapOptions>(options)?;
|
||||
textwrap::wrap(&text, options.get_options())
|
||||
}
|
||||
};
|
||||
|
||||
Ok(lines.iter().map(Cow::to_string).collect::<Vec<String>>())
|
||||
})?;
|
||||
util.set("textwrap", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Find the target layout in the given layout and replace it with the replacement layout,
|
||||
/// returning a new layout.
|
||||
///
|
||||
/// Type: function( layout:[Layout][4], target:[Layout][4], replacement:[Layout][4] ) -> layout:[Layout][4]
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// local layout = {
|
||||
/// Horizontal = {
|
||||
/// splits = {
|
||||
/// "Table", -- Target
|
||||
/// "HelpMenu",
|
||||
/// },
|
||||
/// config = ...,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// xplr.util.layout_replace(layout, "Table", "Selection")
|
||||
/// -- {
|
||||
/// -- Horizontal = {
|
||||
/// -- splits = {
|
||||
/// -- "Selection", -- Replacement
|
||||
/// -- "HelpMenu",
|
||||
/// -- },
|
||||
/// -- config = ...
|
||||
/// -- }
|
||||
/// -- }
|
||||
/// ```
|
||||
pub fn layout_replace<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(
|
||||
move |lua, (layout, target, replacement): (Value, Value, Value)| {
|
||||
let layout: Layout = lua.from_value(layout)?;
|
||||
let target: Layout = lua.from_value(target)?;
|
||||
let replacement: Layout = lua.from_value(replacement)?;
|
||||
|
||||
let res = layout.replace(&target, &replacement);
|
||||
let res = lua::serialize(lua, &res).map_err(LuaError::custom)?;
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
)?;
|
||||
util.set("layout_replace", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Convert [Permission][8] to rwxrwxrwx representation with special bits.
|
||||
///
|
||||
/// Type: function( [Permission][8] ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.permissions_rwx({ user_read = true })
|
||||
/// -- "r--------"
|
||||
///
|
||||
/// xplr.util.permissions_rwx(app.focused_node.permission)
|
||||
/// -- "rwxrwsrwT"
|
||||
/// ```
|
||||
pub fn permissions_rwx<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, permission: Table| {
|
||||
let permissions: Permissions = lua.from_value(Value::Table(permission))?;
|
||||
let permissions = permissions.to_string();
|
||||
Ok(permissions)
|
||||
})?;
|
||||
util.set("permissions_rwx", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Convert [Permission][8] to octal representation.
|
||||
///
|
||||
/// Type: function( [Permission][8] ) -> { number, number, number, number }
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.permissions_octal({ user_read = true })
|
||||
/// -- { 0, 4, 0, 0 }
|
||||
///
|
||||
/// xplr.util.permissions_octal(app.focused_node.permission)
|
||||
/// -- { 0, 7, 5, 4 }
|
||||
/// ```
|
||||
pub fn permissions_octal<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, permission: Table| {
|
||||
let permissions: Permissions = lua.from_value(Value::Table(permission))?;
|
||||
let permissions: Octal = permissions.into();
|
||||
let permissions = lua::serialize(lua, &permissions).map_err(LuaError::custom)?;
|
||||
Ok(permissions)
|
||||
})?;
|
||||
util.set("permissions_octal", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
///
|
||||
/// [1]: https://xplr.dev/en/lua-function-calls#explorer-config
|
||||
/// [2]: https://xplr.dev/en/lua-function-calls#node
|
||||
/// [3]: https://xplr.dev/en/style
|
||||
/// [4]: https://xplr.dev/en/layout
|
||||
/// [5]: https://xplr.dev/en/lua-function-calls#node
|
||||
/// [6]: https://xplr.dev/en/node-type
|
||||
/// [7]: https://xplr.dev/en/node_types
|
||||
/// [8]: https://xplr.dev/en/column-renderer#permission
|
||||
|
||||
pub(crate) fn create_table(lua: &Lua) -> Result<Table> {
|
||||
let mut util = lua.create_table()?;
|
||||
|
||||
util = version(util, lua)?;
|
||||
util = debug(util, lua)?;
|
||||
util = clone(util, lua)?;
|
||||
util = exists(util, lua)?;
|
||||
util = is_dir(util, lua)?;
|
||||
util = is_file(util, lua)?;
|
||||
util = is_symlink(util, lua)?;
|
||||
util = is_absolute(util, lua)?;
|
||||
util = path_split(util, lua)?;
|
||||
util = node(util, lua)?;
|
||||
util = node_type(util, lua)?;
|
||||
util = dirname(util, lua)?;
|
||||
util = basename(util, lua)?;
|
||||
util = absolute(util, lua)?;
|
||||
util = relative_to(util, lua)?;
|
||||
util = shorten(util, lua)?;
|
||||
util = explore(util, lua)?;
|
||||
util = shell_execute(util, lua)?;
|
||||
util = shell_quote(util, lua)?;
|
||||
util = shell_escape(util, lua)?;
|
||||
util = from_json(util, lua)?;
|
||||
util = to_json(util, lua)?;
|
||||
util = from_yaml(util, lua)?;
|
||||
util = to_yaml(util, lua)?;
|
||||
util = lscolor(util, lua)?;
|
||||
util = paint(util, lua)?;
|
||||
util = style_mix(util, lua)?;
|
||||
util = textwrap(util, lua)?;
|
||||
util = layout_replace(util, lua)?;
|
||||
util = permissions_rwx(util, lua)?;
|
||||
util = permissions_octal(util, lua)?;
|
||||
|
||||
Ok(util)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue