diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000..4b89e77546892 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Auto detect text files and perform normalization +* text=auto + +*.rs text diff=rust +*.toml text diff=toml + +*.scm text diff=scheme +*.md text diff=markdown + +book/theme/highlight.js linguist-vendored +Cargo.lock text diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a83a14239c946..b8be15417eff7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,10 +64,12 @@ jobs: rust: stable target: x86_64-pc-windows-msvc cross: false - # - build: aarch64-macos - # os: macos-latest - # rust: stable - # target: aarch64-apple-darwin + - build: aarch64-macos + os: macos-latest + rust: stable + target: aarch64-apple-darwin + cross: false + skip_tests: true # x86_64 host can't run aarch64 code # - build: x86_64-win-gnu # os: windows-2019 # rust: stable-x86_64-gnu @@ -100,6 +102,7 @@ jobs: - name: Run cargo test uses: actions-rs/cargo@v1 + if: "!matrix.skip_tests" with: use-cross: ${{ matrix.cross }} command: test @@ -113,7 +116,7 @@ jobs: args: --release --locked --target ${{ matrix.target }} - name: Strip release binary (linux and macos) - if: matrix.build == 'x86_64-linux' || matrix.build == 'x86_64-macos' + if: matrix.build == 'x86_64-linux' || endsWith(matrix.build, 'macos') run: strip "target/${{ matrix.target }}/release/hx" - name: Strip release binary (arm) diff --git a/.ignore b/.ignore new file mode 100644 index 0000000000000..865856b434980 --- /dev/null +++ b/.ignore @@ -0,0 +1,5 @@ +# Things that we don't want ripgrep to search that we do want in git +# https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#automatic-filtering + +# Minified JS vendored from mdbook +book/theme/highlight.js diff --git a/Cargo.lock b/Cargo.lock index fb94d1e0809f0..d0524e4bc1940 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,21 +13,21 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "c91f1f46651137be86f3a2b9a8359f9ab421d04d941c62b5982e1ca21113adf9" [[package]] name = "arc-swap" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" @@ -48,15 +48,15 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cassowary" @@ -121,12 +121,12 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ "cfg-if", - "lazy_static", + "once_cell", ] [[package]] @@ -178,9 +178,9 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" [[package]] name = "encoding_rs" @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -302,20 +302,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.4" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "globset" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" dependencies = [ "aho-corasick", "bstr", @@ -558,9 +558,9 @@ dependencies = [ [[package]] name = "indoc" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" +checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" [[package]] name = "instant" @@ -573,9 +573,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "lazy_static" @@ -585,9 +585,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.125" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b" [[package]] name = "libloading" @@ -601,10 +601,11 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] @@ -638,9 +639,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" @@ -653,21 +654,21 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.36.1", + "wasi", + "windows-sys", ] [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", @@ -675,9 +676,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -700,9 +701,9 @@ checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "parking_lot" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", @@ -710,15 +711,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.32.0", + "windows-sys", ] [[package]] @@ -729,9 +730,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -741,18 +742,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "pulldown-cmark" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ "bitflags", "memchr", @@ -770,18 +771,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.15" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_core", ] @@ -797,21 +798,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall", + "thiserror", ] [[package]] @@ -848,9 +850,9 @@ dependencies = [ [[package]] name = "retain_mut" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" [[package]] name = "ropey" @@ -864,9 +866,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "same-file" @@ -885,18 +887,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.139" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.139" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +checksum = "34b5b8d809babe02f538c2cfec6f2c1ed10804c0e5a6a041a049a4f5588ccc2e" dependencies = [ "proc-macro2", "quote", @@ -905,9 +907,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" dependencies = [ "itoa", "ryu", @@ -916,9 +918,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", @@ -969,15 +971,18 @@ dependencies = [ [[package]] name = "similar" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3" +checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803" [[package]] name = "slab" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "slotmap" @@ -1029,9 +1034,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "str-buf" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" [[package]] name = "str_indices" @@ -1041,9 +1046,9 @@ checksum = "9d9199fa80c817e074620be84374a520062ebac833f358d74b37060ce4a0f2c0" [[package]] name = "syn" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -1077,18 +1082,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" dependencies = [ "proc-macro2", "quote", @@ -1115,9 +1120,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -1130,10 +1135,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" dependencies = [ + "autocfg", "bytes", "libc", "memchr", @@ -1150,9 +1156,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ "proc-macro2", "quote", @@ -1200,9 +1206,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-general-category" @@ -1212,9 +1218,9 @@ checksum = "1218098468b8085b19a2824104c70d976491d247ce194bbd9dc77181150cdfd6" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-linebreak" @@ -1227,9 +1233,9 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" dependencies = [ "tinyvec", ] @@ -1276,12 +1282,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1330,86 +1330,43 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" -dependencies = [ - "windows_aarch64_msvc 0.32.0", - "windows_i686_gnu 0.32.0", - "windows_i686_msvc 0.32.0", - "windows_x86_64_gnu 0.32.0", - "windows_x86_64_msvc 0.32.0", -] - [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" - [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" -[[package]] -name = "windows_i686_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" - [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" -[[package]] -name = "windows_i686_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" - [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" -[[package]] -name = "windows_x86_64_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" - [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" - [[package]] name = "windows_x86_64_msvc" version = "0.36.1" diff --git a/README.md b/README.md index 48c24de7c7547..d3defc1cffe36 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ # Helix - [![Build status](https://github.com/helix-editor/helix/actions/workflows/build.yml/badge.svg)](https://github.com/helix-editor/helix/actions) ![Screenshot](./screenshot.png) @@ -49,11 +48,11 @@ tree-sitter grammars may be manually fetched and built with `hx --grammar fetch` Helix also needs its runtime files so make sure to copy/symlink the `runtime/` directory into the config directory (for example `~/.config/helix/runtime` on Linux/macOS, or `%AppData%/helix/runtime` on Windows). -| OS | command | -|-------------------|-----------| -|windows(cmd.exe) |`xcopy runtime %AppData%/helix/runtime` | -|windows(powershell)|`xcopy runtime $Env:AppData\helix\runtime` | -|linux/macos |`ln -s $PWD/runtime ~/.config/helix/runtime`| +| OS | Command | +| -------------------- | -------------------------------------------- | +| Windows (cmd.exe) | `xcopy runtime %AppData%\helix\runtime` | +| Windows (PowerShell) | `xcopy runtime $Env:AppData\helix\runtime` | +| Linux/macOS | `ln -s $PWD/runtime ~/.config/helix/runtime` | This location can be overridden via the `HELIX_RUNTIME` environment variable. @@ -77,7 +76,7 @@ Helix can be installed on MacOS through homebrew via: brew tap helix-editor/helix brew install helix ``` - + # Contributing Contributing guidelines can be found [here](./docs/CONTRIBUTING.md). diff --git a/book/src/configuration.md b/book/src/configuration.md index 4eab4a48aa78a..e418869fc223f 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -25,6 +25,9 @@ select = "underline" hidden = false ``` +You may also specify a file to use for configuration with the `-c` or +`--config` CLI argument: `hx -c path/to/custom-config.toml`. + ## Editor ### `[editor]` Section @@ -38,7 +41,7 @@ hidden = false | `shell` | Shell to use when running external commands. | Unix: `["sh", "-c"]`
Windows: `["cmd", "/C"]` | | `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers. | `absolute` | | `cursorline` | Highlight all lines with a cursor. | `false` | -| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `padding`, note that `diagnostics` also includes other features like breakpoints | `["diagnostics", "line-numbers", "padding"]` | +| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "line-numbers"]` | | `auto-completion` | Enable automatic pop up of auto-completion. | `true` | | `auto-format` | Enable automatic formatting on save. | `true` | | `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` | @@ -63,6 +66,7 @@ Statusline elements can be defined as follows: left = ["mode", "spinner"] center = ["file-name"] right = ["diagnostics", "selections", "position", "file-encoding", "file-line-ending", "file-type"] +separator = "│" ``` The following elements can be configured: @@ -78,6 +82,9 @@ The following elements can be configured: | `diagnostics` | The number of warnings and/or errors | | `selections` | The number of active selections | | `position` | The cursor position | +| `position-percentage` | The cursor position as a percentage of the total number of lines | +| `separator` | The string defined in `editor.statusline.separator` (defaults to `"│"`) | +| `spacer` | Inserts a space between elements (multiple/contiguous spacers may be specified) | ### `[editor.lsp]` Section diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 21371c9334f1d..432efb78560bc 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -1,6 +1,7 @@ | Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP | | --- | --- | --- | --- | --- | | bash | ✓ | | | `bash-language-server` | +| beancount | ✓ | | | | | c | ✓ | ✓ | ✓ | `clangd` | | c-sharp | ✓ | | | `OmniSharp` | | cairo | ✓ | | | | @@ -10,9 +11,11 @@ | cpon | ✓ | | ✓ | | | cpp | ✓ | ✓ | ✓ | `clangd` | | css | ✓ | | | `vscode-css-language-server` | +| cue | ✓ | | | `cuelsp` | | dart | ✓ | | ✓ | `dart` | | devicetree | ✓ | | ✓ | | | dockerfile | ✓ | | | `docker-langserver` | +| dot | ✓ | | | `dot-language-server` | | edoc | ✓ | | | | | eex | ✓ | | | | | ejs | ✓ | | | | @@ -34,20 +37,21 @@ | glsl | ✓ | ✓ | ✓ | | | go | ✓ | ✓ | ✓ | `gopls` | | gomod | ✓ | | | `gopls` | +| gotmpl | ✓ | | | `gopls` | | gowork | ✓ | | | `gopls` | | graphql | ✓ | | | | | hare | ✓ | | ✓ | | | haskell | ✓ | | | `haskell-language-server-wrapper` | | hcl | ✓ | | ✓ | `terraform-ls` | -| heex | ✓ | | | | +| heex | ✓ | ✓ | | | | html | ✓ | | | `vscode-html-language-server` | | idris | | | | `idris2-lsp` | | iex | ✓ | | | | | java | ✓ | | | `jdtls` | -| javascript | ✓ | | ✓ | `typescript-language-server` | +| javascript | ✓ | ✓ | ✓ | `typescript-language-server` | | jsdoc | ✓ | | | | | json | ✓ | | ✓ | `vscode-json-language-server` | -| jsx | ✓ | | ✓ | `typescript-language-server` | +| jsx | ✓ | ✓ | ✓ | `typescript-language-server` | | julia | ✓ | | | `julia` | | kotlin | ✓ | | | `kotlin-language-server` | | latex | ✓ | | | `texlab` | @@ -59,6 +63,7 @@ | lua | ✓ | | ✓ | `lua-language-server` | | make | ✓ | | | | | markdown | ✓ | | | | +| markdown.inline | ✓ | | | | | meson | ✓ | | ✓ | | | mint | | | | `mint` | | nickel | ✓ | | ✓ | `nls` | @@ -66,7 +71,7 @@ | nu | ✓ | | | | | ocaml | ✓ | | ✓ | `ocamllsp` | | ocaml-interface | ✓ | | | `ocamllsp` | -| odin | ✓ | | | | +| odin | ✓ | | | `ols` | | openscad | ✓ | | | `openscad-language-server` | | org | ✓ | | | | | perl | ✓ | ✓ | ✓ | | @@ -86,6 +91,7 @@ | scala | ✓ | | ✓ | `metals` | | scheme | ✓ | | | | | scss | ✓ | | | `vscode-css-language-server` | +| slint | ✓ | | ✓ | `slint-lsp` | | solidity | ✓ | | | `solc` | | sql | ✓ | | | | | sshclientconfig | ✓ | | | | @@ -96,9 +102,9 @@ | tfvars | | | | `terraform-ls` | | toml | ✓ | | | `taplo` | | tsq | ✓ | | | | -| tsx | ✓ | | | `typescript-language-server` | +| tsx | ✓ | ✓ | ✓ | `typescript-language-server` | | twig | ✓ | | | | -| typescript | ✓ | | ✓ | `typescript-language-server` | +| typescript | ✓ | ✓ | ✓ | `typescript-language-server` | | ungrammar | ✓ | | | | | v | ✓ | | | `vls` | | vala | ✓ | | | `vala-language-server` | diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 6e6beab4b40fb..653acf60cac1f 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -1,18 +1,18 @@ | Name | Description | | --- | --- | | `:quit`, `:q` | Close the current view. | -| `:quit!`, `:q!` | Close the current view forcefully (ignoring unsaved changes). | +| `:quit!`, `:q!` | Force close the current view, ignoring unsaved changes. | | `:open`, `:o` | Open a file from disk into the current view. | | `:buffer-close`, `:bc`, `:bclose` | Close the current buffer. | -| `:buffer-close!`, `:bc!`, `:bclose!` | Close the current buffer forcefully (ignoring unsaved changes). | +| `:buffer-close!`, `:bc!`, `:bclose!` | Close the current buffer forcefully, ignoring unsaved changes. | | `:buffer-close-others`, `:bco`, `:bcloseother` | Close all buffers but the currently focused one. | -| `:buffer-close-others!`, `:bco!`, `:bcloseother!` | Close all buffers but the currently focused one. | -| `:buffer-close-all`, `:bca`, `:bcloseall` | Close all buffers, without quitting. | -| `:buffer-close-all!`, `:bca!`, `:bcloseall!` | Close all buffers forcefully (ignoring unsaved changes), without quitting. | -| `:buffer-next`, `:bn`, `:bnext` | Go to next buffer. | -| `:buffer-previous`, `:bp`, `:bprev` | Go to previous buffer. | +| `:buffer-close-others!`, `:bco!`, `:bcloseother!` | Force close all buffers but the currently focused one. | +| `:buffer-close-all`, `:bca`, `:bcloseall` | Close all buffers without quitting. | +| `:buffer-close-all!`, `:bca!`, `:bcloseall!` | Force close all buffers ignoring unsaved changes without quitting. | +| `:buffer-next`, `:bn`, `:bnext` | Goto next buffer. | +| `:buffer-previous`, `:bp`, `:bprev` | Goto previous buffer. | | `:write`, `:w` | Write changes to disk. Accepts an optional path (:write some/path.txt) | -| `:write!`, `:w!` | Write changes to disk forcefully (creating necessary subdirectories). Accepts an optional path (:write some/path.txt) | +| `:write!`, `:w!` | Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write some/path.txt) | | `:new`, `:n` | Create a new scratch buffer. | | `:format`, `:fmt` | Format the file using the LSP formatter. | | `:indent-style` | Set the indentation style for editing. ('t' for tabs or 1-8 for number of spaces.) | @@ -25,9 +25,9 @@ | `:write-quit-all`, `:wqa`, `:xa` | Write changes from all buffers to disk and close all views. | | `:write-quit-all!`, `:wqa!`, `:xa!` | Write changes from all buffers to disk and close all views forcefully (ignoring unsaved changes). | | `:quit-all`, `:qa` | Close all views. | -| `:quit-all!`, `:qa!` | Close all views forcefully (ignoring unsaved changes). | +| `:quit-all!`, `:qa!` | Force close all views ignoring unsaved changes. | | `:cquit`, `:cq` | Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2). | -| `:cquit!`, `:cq!` | Quit with exit code (default 1) forcefully (ignoring unsaved changes). Accepts an optional integer exit code (:cq! 2). | +| `:cquit!`, `:cq!` | Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2). | | `:theme` | Change the editor theme. | | `:clipboard-yank` | Yank main selection into system clipboard. | | `:clipboard-yank-join` | Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline. | @@ -42,7 +42,7 @@ | `:show-clipboard-provider` | Show clipboard provider name in status bar. | | `:change-current-directory`, `:cd` | Change the current working directory. | | `:show-directory`, `:pwd` | Show the current working directory. | -| `:encoding` | Set encoding based on `https://encoding.spec.whatwg.org` | +| `:encoding` | Set encoding. Based on `https://encoding.spec.whatwg.org`. | | `:reload` | Discard changes and reload from the source file. | | `:tree-sitter-scopes` | Display tree sitter scopes, primarily for theming and development. | | `:debug-start`, `:dbg` | Start a debug session from a given template with given parameters. | @@ -53,7 +53,7 @@ | `:hsplit`, `:hs`, `:sp` | Open the file in a horizontal split. | | `:hsplit-new`, `:hnew` | Open a scratch buffer in a horizontal split. | | `:tutor` | Open the tutorial. | -| `:goto`, `:g` | Go to line number. | +| `:goto`, `:g` | Goto line number. | | `:set-language`, `:lang` | Set the language of current buffer. | | `:set-option`, `:set` | Set a config option at runtime.
For example to disable smart case search, use `:set search.smart-case false`. | | `:get-option`, `:get` | Get the current value of a config option. | @@ -61,8 +61,8 @@ | `:rsort` | Sort ranges in selection in reverse order. | | `:reflow` | Hard-wrap the current selection of lines to a given width. | | `:tree-sitter-subtree`, `:ts-subtree` | Display tree sitter subtree under cursor, primarily for debugging queries. | -| `:config-reload` | Refreshes helix's config. | -| `:config-open` | Open the helix config.toml file. | +| `:config-reload` | Refresh user config. | +| `:config-open` | Open the user config.toml file. | | `:log-open` | Open the helix log file. | | `:insert-output` | Run shell command, inserting output after each selection. | | `:append-output` | Run shell command, appending output after each selection. | diff --git a/book/src/keymap.md b/book/src/keymap.md index 9acbd3b6b49c8..25f1943de9d20 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -1,7 +1,27 @@ # Keymap -- Mappings marked (**LSP**) require an active language server for the file. -- Mappings marked (**TS**) require a tree-sitter grammar for the filetype. +- [Normal mode](#normal-mode) + - [Movement](#movement) + - [Changes](#changes) + - [Shell](#shell) + - [Selection manipulation](#selection-manipulation) + - [Search](#search) + - [Minor modes](#minor-modes) + - [View mode](#view-mode) + - [Goto mode](#goto-mode) + - [Match mode](#match-mode) + - [Window mode](#window-mode) + - [Space mode](#space-mode) + - [Popup](#popup) + - [Unimpaired](#unimpaired) +- [Insert Mode](#insert-mode) +- [Select / extend mode](#select--extend-mode) +- [Picker](#picker) +- [Prompt](#prompt) + +> 💡 Mappings marked (**LSP**) require an active language server for the file. + +> 💡 Mappings marked (**TS**) require a tree-sitter grammar for the filetype. ## Normal mode @@ -337,7 +357,7 @@ mode before pressing `n` or `N` makes it possible to keep the current selection. Toggling it on and off during your iterative searching allows you to selectively add search terms to your selections. -# Picker +## Picker Keys to use within picker. Remapping currently not supported. @@ -356,7 +376,7 @@ Keys to use within picker. Remapping currently not supported. | `Ctrl-t` | Toggle preview | | `Escape`, `Ctrl-c` | Close picker | -# Prompt +## Prompt Keys to use within prompt, Remapping currently not supported. diff --git a/book/src/languages.md b/book/src/languages.md index a9d5bea834365..841b1377d0351 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -40,6 +40,7 @@ file-types = ["mylang", "myl"] comment-token = "#" indent = { tab-width = 2, unit = " " } language-server = { command = "mylang-lsp", args = ["--stdio"] } +formatter = { command = "mylang-formatter" , args = ["--stdin"] } ``` These configuration keys are available: @@ -59,6 +60,7 @@ These configuration keys are available: | `language-server` | The Language Server to run. See the Language Server configuration section below. | | `config` | Language Server configuration | | `grammar` | The tree-sitter grammar to use (defaults to the value of `name`) | +| `formatter` | The formatter for the language, it will take precedence over the lsp when defined. The formatter must be able to take the original file as input from stdin and write the formatted file to stdout | ### Language Server configuration diff --git a/book/src/themes.md b/book/src/themes.md index ad8864b20d579..e03782db37958 100644 --- a/book/src/themes.md +++ b/book/src/themes.md @@ -224,6 +224,7 @@ These scopes are used for theming the editor interface. | `ui.statusline.normal` | Statusline mode during normal mode ([only if `editor.color-modes` is enabled][editor-section]) | | `ui.statusline.insert` | Statusline mode during insert mode ([only if `editor.color-modes` is enabled][editor-section]) | | `ui.statusline.select` | Statusline mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) | +| `ui.statusline.separator` | Separator character in statusline | | `ui.popup` | Documentation popups (e.g space-k) | | `ui.popup.info` | Prompt for multiple key options | | `ui.window` | Border lines separating splits | diff --git a/contrib/completion/hx.bash b/contrib/completion/hx.bash index 87c340284f6e3..8a2d9777bc3f5 100644 --- a/contrib/completion/hx.bash +++ b/contrib/completion/hx.bash @@ -16,7 +16,7 @@ _hx() { COMPREPLY=($(compgen -W "$languages" -- $2)) ;; *) - COMPREPLY=($(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit" -- $2)) + COMPREPLY=($(compgen -fd -W "-h --help --tutor -V --version -v -vv -vvv --health -g --grammar --vsplit --hsplit -c --config" -- $2)) ;; esac } && complete -F _hx hx diff --git a/contrib/completion/hx.elv b/contrib/completion/hx.elv new file mode 100644 index 0000000000000..d3d227bcb566a --- /dev/null +++ b/contrib/completion/hx.elv @@ -0,0 +1,49 @@ +# You can move it here ~/.config/elvish/lib/hx.elv +# Or add `eval (slurp < ~/$REPOS/helix/contrib/completion/hx.elv)` +# Be sure to replace `$REPOS` with something that makes sense for you! + +### Renders a pretty completion candidate +var candidate = { | _stem _desc | + edit:complex-candidate $_stem &display=(styled $_stem bold)(styled " "$_desc dim) +} + +### These commands will invalidate further input (i.e. not react to them) +var skips = [ "--tutor" "--help" "--version" "-V" "--health" ] + +### Grammar commands +var grammar = [ "--grammar" "-g" ] + +### Config commands +var config = [ "--config" "-c" ] + +### Set an arg-completer for the `hx` binary +set edit:completion:arg-completer[hx] = {|@args| + var n = (count $args) + if (>= $n 3) { + # Stop completions if passed arg will take presedence + # and invalidate further input + if (has-value $skips $args[-2]) { + return + } + # If the previous arg == --grammar, then only suggest: + if (has-value $grammar $args[-2]) { + $candidate "fetch" "Fetch the tree-sitter grammars" + $candidate "build" "Build the tree-sitter grammars" + return + } + # When we have --config, we need a file + if (has-values $config $args[-2]) { + edit:complete-filename $args[-1] | each { |v| put $v[stem] } + return + } + } + edit:complete-filename $args[-1] | each { |v| put $v[stem]} + $candidate "--help" "(Prints help information)" + $candidate "--version" "(Prints version information)" + $candidate "--tutor" "(Loads the tutorial)" + $candidate "--health" "(Checks for errors in editor setup)" + $candidate "--grammar" "(Fetch or build the tree-sitter grammars)" + $candidate "--vsplit" "(Splits all given files vertically)" + $candidate "--hsplit" "(Splits all given files horizontally)" + $candidate "--config" "(Specifies a file to use for configuration)" +} \ No newline at end of file diff --git a/contrib/completion/hx.fish b/contrib/completion/hx.fish index df2fb500c935f..65f248d4234d6 100644 --- a/contrib/completion/hx.fish +++ b/contrib/completion/hx.fish @@ -11,3 +11,4 @@ complete -c hx -s v -o vv -o vvv -d "Increases logging verbosity" complete -c hx -s V -l version -d "Prints version information" complete -c hx -l vsplit -d "Splits all given files vertically into different windows" complete -c hx -l hsplit -d "Splits all given files horizontally into different windows" +complete -c hx -s c -l config -d "Specifies a file to use for completion" diff --git a/contrib/completion/hx.zsh b/contrib/completion/hx.zsh index f9d58d3c6569b..e3375656d65ac 100644 --- a/contrib/completion/hx.zsh +++ b/contrib/completion/hx.zsh @@ -16,6 +16,8 @@ _hx() { "--grammar[Fetches or builds tree-sitter grammars]:action:->grammar" \ "--vsplit[Splits all given files vertically into different windows]" \ "--hsplit[Splits all given files horizontally into different windows]" \ + "-c[Specifies a file to use for configuration]" \ + "--config[Specifies a file to use for configuration]" \ "*:file:_files" case "$state" in diff --git a/flake.nix b/flake.nix index fdeed2aaf2993..7b6f0685d472c 100644 --- a/flake.nix +++ b/flake.nix @@ -18,83 +18,117 @@ nixpkgs, nixCargoIntegration, ... - }: - nixCargoIntegration.lib.makeOutputs { - root = ./.; - renameOutputs = {"helix-term" = "helix";}; - # Set default app to hx (binary is from helix-term release build) - # Set default package to helix-term release build - defaultOutputs = { - app = "hx"; - package = "helix"; - }; - overrides = { - cCompiler = common: - with common.pkgs; - if stdenv.isLinux - then gcc - else clang; - crateOverrides = common: _: { - helix-term = prev: let - inherit (common) pkgs; - mkRootPath = rel: - builtins.path { - path = "${common.root}/${rel}"; - name = rel; - }; - grammars = pkgs.callPackage ./grammars.nix {}; - runtimeDir = pkgs.runCommandNoCC "helix-runtime" {} '' - mkdir -p $out - ln -s ${mkRootPath "runtime"}/* $out - rm -r $out/grammars - ln -s ${grammars} $out/grammars - ''; - in { - # disable fetching and building of tree-sitter grammars in the helix-term build.rs - HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1"; - # link languages and theme toml files since helix-term expects them (for tests) - preConfigure = - pkgs.lib.concatMapStringsSep - "\n" - (path: "ln -sf ${mkRootPath path} ..") - ["languages.toml" "theme.toml" "base16_theme.toml"]; - buildInputs = (prev.buildInputs or []) ++ [common.cCompiler.cc.lib]; - nativeBuildInputs = [pkgs.makeWrapper]; + }: let + outputs = config: + nixCargoIntegration.lib.makeOutputs { + root = ./.; + renameOutputs = {"helix-term" = "helix";}; + # Set default app to hx (binary is from helix-term release build) + # Set default package to helix-term release build + defaultOutputs = { + app = "hx"; + package = "helix"; + }; + overrides = { + cCompiler = common: + with common.pkgs; + if stdenv.isLinux + then gcc + else clang; + crateOverrides = common: _: { + helix-term = prev: let + inherit (common) pkgs; + mkRootPath = rel: + builtins.path { + path = "${common.root}/${rel}"; + name = rel; + }; + grammars = pkgs.callPackage ./grammars.nix config; + runtimeDir = pkgs.runCommandNoCC "helix-runtime" {} '' + mkdir -p $out + ln -s ${mkRootPath "runtime"}/* $out + rm -r $out/grammars + ln -s ${grammars} $out/grammars + ''; + overridedAttrs = { + # disable fetching and building of tree-sitter grammars in the helix-term build.rs + HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1"; + # link languages and theme toml files since helix-term expects them (for tests) + preConfigure = + pkgs.lib.concatMapStringsSep + "\n" + (path: "ln -sf ${mkRootPath path} ..") + ["languages.toml" "theme.toml" "base16_theme.toml"]; + buildInputs = (prev.buildInputs or []) ++ [common.cCompiler.cc.lib]; + nativeBuildInputs = [pkgs.makeWrapper]; - postFixup = '' - if [ -f "$out/bin/hx" ]; then - wrapProgram "$out/bin/hx" ''${makeWrapperArgs[@]} --set HELIX_RUNTIME "${runtimeDir}" - fi - ''; + postFixup = '' + if [ -f "$out/bin/hx" ]; then + wrapProgram "$out/bin/hx" ''${makeWrapperArgs[@]} --set HELIX_RUNTIME "${runtimeDir}" + fi + ''; + }; + in + overridedAttrs + // ( + pkgs.lib.optionalAttrs + (config ? makeWrapperArgs) + {inherit (config) makeWrapperArgs;} + ); + }; + shell = common: prev: { + packages = + prev.packages + ++ ( + with common.pkgs; + [lld_13 lldb cargo-flamegraph rust-analyzer] ++ + (lib.optional (stdenv.isx86_64 && stdenv.isLinux) cargo-tarpaulin) + ); + env = + prev.env + ++ [ + { + name = "HELIX_RUNTIME"; + eval = "$PWD/runtime"; + } + { + name = "RUST_BACKTRACE"; + value = "1"; + } + { + name = "RUSTFLAGS"; + value = + if common.pkgs.stdenv.isLinux + then "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment" + else ""; + } + ]; }; }; - shell = common: prev: { - packages = - prev.packages - ++ ( - with common.pkgs; [lld_13 lldb cargo-tarpaulin cargo-flamegraph rust-analyzer] - ); - env = - prev.env - ++ [ - { - name = "HELIX_RUNTIME"; - eval = "$PWD/runtime"; - } - { - name = "RUST_BACKTRACE"; - value = "1"; - } - { - name = "RUSTFLAGS"; - value = - if common.pkgs.stdenv.isLinux - then "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment" - else ""; - } - ]; - }; }; + defaultOutputs = outputs {}; + makeOverridableHelix = system: old: + old + // { + override = args: + makeOverridableHelix + system + (outputs args).packages.${system}.helix; + }; + in + defaultOutputs + // { + packages = + nixpkgs.lib.mapAttrs + ( + system: packages: + packages + // rec { + default = helix; + helix = makeOverridableHelix system packages.helix; + } + ) + defaultOutputs.packages; }; nixConfig = { diff --git a/grammars.nix b/grammars.nix index 2f50662eb72ff..066fa69da0c1b 100644 --- a/grammars.nix +++ b/grammars.nix @@ -4,6 +4,8 @@ runCommandLocal, runCommandNoCC, yj, + includeGrammarIf ? _: true, + ... }: let # HACK: nix < 2.6 has a bug in the toml parser, so we convert to JSON # before parsing @@ -102,12 +104,13 @@ runHook postFixup ''; }; + grammarsToBuild = builtins.filter includeGrammarIf gitGrammars; builtGrammars = builtins.map (grammar: { inherit (grammar) name; artifact = buildGrammar grammar; }) - gitGrammars; + grammarsToBuild; grammarLinks = builtins.map (grammar: "ln -s ${grammar.artifact}/${grammar.name}.so $out/${grammar.name}.so") builtGrammars; diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index b7a1f33230d31..0bae7b899b0cc 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -35,7 +35,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" toml = "0.5" -similar = "2.1" +similar = "2.2" encoding_rs = "0.8" diff --git a/helix-core/src/diagnostic.rs b/helix-core/src/diagnostic.rs index 210ad639197de..48a68dc02d2dd 100644 --- a/helix-core/src/diagnostic.rs +++ b/helix-core/src/diagnostic.rs @@ -23,6 +23,12 @@ pub struct Range { pub end: usize, } +#[derive(Debug, Eq, Hash, PartialEq, Clone, Deserialize, Serialize)] +pub enum NumberOrString { + Number(i32), + String(String), +} + /// Corresponds to [`lsp_types::Diagnostic`](https://docs.rs/lsp-types/0.91.0/lsp_types/struct.Diagnostic.html) #[derive(Debug, Clone)] pub struct Diagnostic { @@ -30,4 +36,5 @@ pub struct Diagnostic { pub line: usize, pub message: String, pub severity: Option, + pub code: Option, } diff --git a/helix-core/src/increment/date_time.rs b/helix-core/src/increment/date_time.rs index 91fa596316039..1574bf4d2e467 100644 --- a/helix-core/src/increment/date_time.rs +++ b/helix-core/src/increment/date_time.rs @@ -5,6 +5,7 @@ use ropey::RopeSlice; use std::borrow::Cow; use std::cmp; +use std::fmt::Write; use super::Increment; use crate::{Range, Tendril}; @@ -162,7 +163,7 @@ impl Format { fields.push(field); max_len += field.max_len + remaining[..i].len(); regex += &remaining[..i]; - regex += &format!("({})", field.regex); + write!(regex, "({})", field.regex).unwrap(); remaining = &after[spec_len..]; } diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 83bab5e3020f0..59bd736e60fcb 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -222,9 +222,23 @@ impl Range { // groupAt + /// Returns the text inside this range given the text of the whole buffer. + /// + /// The returned `Cow` is a reference if the range of text is inside a single + /// chunk of the rope. Otherwise a copy of the text is returned. Consider + /// using `slice` instead if you do not need a `Cow` or `String` to avoid copying. #[inline] pub fn fragment<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> Cow<'b, str> { - text.slice(self.from()..self.to()).into() + self.slice(text).into() + } + + /// Returns the text inside this range given the text of the whole buffer. + /// + /// The returned value is a reference to the passed slice. This method never + /// copies any contents. + #[inline] + pub fn slice<'a, 'b: 'a>(&'a self, text: RopeSlice<'b>) -> RopeSlice<'b> { + text.slice(self.from()..self.to()) } //-------------------------------- @@ -548,6 +562,10 @@ impl Selection { self.ranges.iter().map(move |range| range.fragment(text)) } + pub fn slices<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator + 'a { + self.ranges.iter().map(move |range| range.slice(text)) + } + #[inline(always)] pub fn iter(&self) -> std::slice::Iter<'_, Range> { self.ranges.iter() diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 9011f835b9076..99922d3774c1f 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -79,6 +79,9 @@ pub struct LanguageConfiguration { #[serde(default)] pub auto_format: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub formatter: Option, + #[serde(default)] pub diagnostic_severity: Severity, @@ -126,6 +129,15 @@ pub struct LanguageServerConfiguration { pub language_id: Option, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct FormatterConfiguration { + pub command: String, + #[serde(default)] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub args: Vec, +} + #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct AdvancedCompletion { @@ -752,7 +764,7 @@ impl Syntax { ); let mut injections = Vec::new(); for mat in matches { - let (language_name, content_node, include_children) = injection_for_match( + let (language_name, content_node, included_children) = injection_for_match( &layer.config, &layer.config.injections_query, &mat, @@ -769,7 +781,7 @@ impl Syntax { { if let Some(config) = (injection_callback)(&language_name) { let ranges = - intersect_ranges(&layer.ranges, &[content_node], include_children); + intersect_ranges(&layer.ranges, &[content_node], included_children); if !ranges.is_empty() { injections.push((config, ranges)); @@ -781,7 +793,10 @@ impl Syntax { // Process combined injections. if let Some(combined_injections_query) = &layer.config.combined_injections_query { let mut injections_by_pattern_index = - vec![(None, Vec::new(), false); combined_injections_query.pattern_count()]; + vec![ + (None, Vec::new(), IncludedChildren::default()); + combined_injections_query.pattern_count() + ]; let matches = cursor.matches( combined_injections_query, layer.tree().root_node(), @@ -789,7 +804,7 @@ impl Syntax { ); for mat in matches { let entry = &mut injections_by_pattern_index[mat.pattern_index]; - let (language_name, content_node, include_children) = injection_for_match( + let (language_name, content_node, included_children) = injection_for_match( &layer.config, combined_injections_query, &mat, @@ -801,16 +816,16 @@ impl Syntax { if let Some(content_node) = content_node { entry.1.push(content_node); } - entry.2 = include_children; + entry.2 = included_children; } - for (lang_name, content_nodes, includes_children) in injections_by_pattern_index + for (lang_name, content_nodes, included_children) in injections_by_pattern_index { if let (Some(lang_name), false) = (lang_name, content_nodes.is_empty()) { if let Some(config) = (injection_callback)(&lang_name) { let ranges = intersect_ranges( &layer.ranges, &content_nodes, - includes_children, + included_children, ); if !ranges.is_empty() { injections.push((config, ranges)); @@ -1341,8 +1356,8 @@ impl HighlightConfiguration { /// Tree-sitter syntax-highlighting queries specify highlights in the form of dot-separated /// highlight names like `punctuation.bracket` and `function.method.builtin`. Consumers of /// these queries can choose to recognize highlights with different levels of specificity. - /// For example, the string `function.builtin` will match against `function.method.builtin` - /// and `function.builtin.constructor`, but will not match `function.method`. + /// For example, the string `function.builtin` will match against `function.builtin.constructor` + /// but will not match `function.method.builtin` and `function.method`. /// /// When highlighting, results are returned as `Highlight` values, which contain the index /// of the matched highlight this list of highlight names. @@ -1362,11 +1377,13 @@ impl HighlightConfiguration { let recognized_name = recognized_name; let mut len = 0; let mut matches = true; - for part in recognized_name.split('.') { - len += 1; - if !capture_parts.contains(&part) { - matches = false; - break; + for (i, part) in recognized_name.split('.').enumerate() { + match capture_parts.get(i) { + Some(capture_part) if *capture_part == part => len += 1, + _ => { + matches = false; + break; + } } } if matches && len > best_match_len { @@ -1408,6 +1425,19 @@ impl<'a> HighlightIterLayer<'a> { } } +#[derive(Clone)] +enum IncludedChildren { + None, + All, + Unnamed, +} + +impl Default for IncludedChildren { + fn default() -> Self { + Self::None + } +} + // Compute the ranges that should be included when parsing an injection. // This takes into account three things: // * `parent_ranges` - The ranges must all fall within the *current* layer's ranges. @@ -1420,7 +1450,7 @@ impl<'a> HighlightIterLayer<'a> { fn intersect_ranges( parent_ranges: &[Range], nodes: &[Node], - includes_children: bool, + included_children: IncludedChildren, ) -> Vec { let mut cursor = nodes[0].walk(); let mut result = Vec::new(); @@ -1444,11 +1474,15 @@ fn intersect_ranges( for excluded_range in node .children(&mut cursor) - .filter_map(|child| { - if includes_children { - None - } else { - Some(child.range()) + .filter_map(|child| match included_children { + IncludedChildren::None => Some(child.range()), + IncludedChildren::All => None, + IncludedChildren::Unnamed => { + if child.is_named() { + Some(child.range()) + } else { + None + } } }) .chain([following_range].iter().cloned()) @@ -1777,7 +1811,7 @@ fn injection_for_match<'a>( query: &'a Query, query_match: &QueryMatch<'a, 'a>, source: RopeSlice<'a>, -) -> (Option>, Option>, bool) { +) -> (Option>, Option>, IncludedChildren) { let content_capture_index = config.injection_content_capture_index; let language_capture_index = config.injection_language_capture_index; @@ -1793,7 +1827,7 @@ fn injection_for_match<'a>( } } - let mut include_children = false; + let mut included_children = IncludedChildren::default(); for prop in query.property_settings(query_match.pattern_index) { match prop.key.as_ref() { // In addition to specifying the language name via the text of a @@ -1809,12 +1843,17 @@ fn injection_for_match<'a>( // `injection.content` node - only the ranges that belong to the // node itself. This can be changed using a `#set!` predicate that // sets the `injection.include-children` key. - "injection.include-children" => include_children = true, + "injection.include-children" => included_children = IncludedChildren::All, + + // Some queries might only exclude named children but include unnamed + // children in their `injection.content` node. This can be enabled using + // a `#set!` predicate that sets the `injection.include-unnamed-children` key. + "injection.include-unnamed-children" => included_children = IncludedChildren::Unnamed, _ => {} } } - (language_name, content_node, include_children) + (language_name, content_node, included_children) } pub struct Merge { diff --git a/helix-loader/src/config.rs b/helix-loader/src/config.rs index a8c843612811c..259b1318ea00b 100644 --- a/helix-loader/src/config.rs +++ b/helix-loader/src/config.rs @@ -19,7 +19,23 @@ pub fn user_lang_config() -> Result { .into_iter() .chain([default_lang_config()].into_iter()) .fold(toml::Value::Table(toml::value::Table::default()), |a, b| { - crate::merge_toml_values(b, a, true) + // combines for example + // b: + // [[language]] + // name = "toml" + // language-server = { command = "taplo", args = ["lsp", "stdio"] } + // + // a: + // [[language]] + // language-server = { command = "/usr/bin/taplo" } + // + // into: + // [[language]] + // name = "toml" + // language-server = { command = "/usr/bin/taplo" } + // + // thus it overrides the third depth-level of b with values of a if they exist, but otherwise merges their values + crate::merge_toml_values(b, a, 3) }); Ok(config) diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs index 7aa9bc8346e17..231ecf348f407 100644 --- a/helix-loader/src/grammar.rs +++ b/helix-loader/src/grammar.rs @@ -92,8 +92,12 @@ pub fn fetch_grammars() -> Result<()> { run_parallel(grammars, fetch_grammar, "fetch") } -pub fn build_grammars() -> Result<()> { - run_parallel(get_grammar_configs()?, build_grammar, "build") +pub fn build_grammars(target: Option) -> Result<()> { + run_parallel( + get_grammar_configs()?, + move |grammar| build_grammar(grammar, target.as_deref()), + "build", + ) } // Returns the set of grammar configurations the user requests. @@ -124,13 +128,14 @@ fn get_grammar_configs() -> Result> { fn run_parallel(grammars: Vec, job: F, action: &'static str) -> Result<()> where - F: Fn(GrammarConfiguration) -> Result<()> + std::marker::Send + 'static + Copy, + F: Fn(GrammarConfiguration) -> Result<()> + std::marker::Send + 'static + Clone, { let pool = threadpool::Builder::new().build(); let (tx, rx) = channel(); for grammar in grammars { let tx = tx.clone(); + let job = job.clone(); pool.execute(move || { // Ignore any SendErrors, if any job in another thread has encountered an @@ -240,7 +245,7 @@ where } } -fn build_grammar(grammar: GrammarConfiguration) -> Result<()> { +fn build_grammar(grammar: GrammarConfiguration, target: Option<&str>) -> Result<()> { let grammar_dir = if let GrammarSource::Local { path } = &grammar.source { PathBuf::from(&path) } else { @@ -273,10 +278,14 @@ fn build_grammar(grammar: GrammarConfiguration) -> Result<()> { } .join("src"); - build_tree_sitter_library(&path, grammar) + build_tree_sitter_library(&path, grammar, target) } -fn build_tree_sitter_library(src_path: &Path, grammar: GrammarConfiguration) -> Result<()> { +fn build_tree_sitter_library( + src_path: &Path, + grammar: GrammarConfiguration, + target: Option<&str>, +) -> Result<()> { let header_path = src_path; let parser_path = src_path.join("parser.c"); let mut scanner_path = src_path.join("scanner.c"); @@ -311,15 +320,16 @@ fn build_tree_sitter_library(src_path: &Path, grammar: GrammarConfiguration) -> .opt_level(3) .cargo_metadata(false) .host(BUILD_TARGET) - .target(BUILD_TARGET); + .target(target.unwrap_or(BUILD_TARGET)); let compiler = config.get_compiler(); let mut command = Command::new(compiler.path()); command.current_dir(src_path); for (key, value) in compiler.env() { command.env(key, value); } + command.args(compiler.args()); - if cfg!(windows) { + if cfg!(all(windows, target_env = "msvc")) { command .args(&["/nologo", "/LD", "/I"]) .arg(header_path) diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index ff4414b2647d2..015b39a5911f3 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -2,11 +2,28 @@ pub mod config; pub mod grammar; use etcetera::base_strategy::{choose_base_strategy, BaseStrategy}; +use std::path::PathBuf; -pub static RUNTIME_DIR: once_cell::sync::Lazy = - once_cell::sync::Lazy::new(runtime_dir); +pub static RUNTIME_DIR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(runtime_dir); -pub fn runtime_dir() -> std::path::PathBuf { +static CONFIG_FILE: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); + +pub fn initialize_config_file(specified_file: Option) { + let config_file = specified_file.unwrap_or_else(|| { + let config_dir = config_dir(); + + if !config_dir.exists() { + std::fs::create_dir_all(&config_dir).ok(); + } + + config_dir.join("config.toml") + }); + + // We should only initialize this value once. + CONFIG_FILE.set(config_file).ok(); +} + +pub fn runtime_dir() -> PathBuf { if let Ok(dir) = std::env::var("HELIX_RUNTIME") { return dir.into(); } @@ -31,7 +48,7 @@ pub fn runtime_dir() -> std::path::PathBuf { .unwrap() } -pub fn config_dir() -> std::path::PathBuf { +pub fn config_dir() -> PathBuf { // TODO: allow env var override let strategy = choose_base_strategy().expect("Unable to find the config directory!"); let mut path = strategy.config_dir(); @@ -39,7 +56,7 @@ pub fn config_dir() -> std::path::PathBuf { path } -pub fn local_config_dirs() -> Vec { +pub fn local_config_dirs() -> Vec { let directories = find_root_impl(None, &[".helix".to_string()]) .into_iter() .map(|path| path.join(".helix")) @@ -48,7 +65,7 @@ pub fn local_config_dirs() -> Vec { directories } -pub fn cache_dir() -> std::path::PathBuf { +pub fn cache_dir() -> PathBuf { // TODO: allow env var override let strategy = choose_base_strategy().expect("Unable to find the config directory!"); let mut path = strategy.cache_dir(); @@ -56,19 +73,22 @@ pub fn cache_dir() -> std::path::PathBuf { path } -pub fn config_file() -> std::path::PathBuf { - config_dir().join("config.toml") +pub fn config_file() -> PathBuf { + CONFIG_FILE + .get() + .map(|path| path.to_path_buf()) + .unwrap_or_else(|| config_dir().join("config.toml")) } -pub fn lang_config_file() -> std::path::PathBuf { +pub fn lang_config_file() -> PathBuf { config_dir().join("languages.toml") } -pub fn log_file() -> std::path::PathBuf { +pub fn log_file() -> PathBuf { cache_dir().join("helix.log") } -pub fn find_root_impl(root: Option<&str>, root_markers: &[String]) -> Vec { +pub fn find_root_impl(root: Option<&str>, root_markers: &[String]) -> Vec { let current_dir = std::env::current_dir().expect("unable to determine current directory"); let mut directories = Vec::new(); @@ -113,11 +133,7 @@ pub fn find_root_impl(root: Option<&str>, root_markers: &[String]) -> Vec toml::Value { +pub fn merge_toml_values(left: toml::Value, right: toml::Value, merge_depth: usize) -> toml::Value { use toml::Value; fn get_name(v: &Value) -> Option<&str> { @@ -131,7 +147,7 @@ pub fn merge_toml_values( // that you can specify a sub-set of languages in an overriding // `languages.toml` but that nested arrays like Language Server // arguments are replaced instead of merged. - if merge_toplevel_arrays { + if merge_depth > 0 { left_items.reserve(right_items.len()); for rvalue in right_items { let lvalue = get_name(&rvalue) @@ -140,7 +156,7 @@ pub fn merge_toml_values( }) .map(|lpos| left_items.remove(lpos)); let mvalue = match lvalue { - Some(lvalue) => merge_toml_values(lvalue, rvalue, false), + Some(lvalue) => merge_toml_values(lvalue, rvalue, merge_depth - 1), None => rvalue, }; left_items.push(mvalue); @@ -151,18 +167,22 @@ pub fn merge_toml_values( } } (Value::Table(mut left_map), Value::Table(right_map)) => { - for (rname, rvalue) in right_map { - match left_map.remove(&rname) { - Some(lvalue) => { - let merged_value = merge_toml_values(lvalue, rvalue, merge_toplevel_arrays); - left_map.insert(rname, merged_value); - } - None => { - left_map.insert(rname, rvalue); + if merge_depth > 0 { + for (rname, rvalue) in right_map { + match left_map.remove(&rname) { + Some(lvalue) => { + let merged_value = merge_toml_values(lvalue, rvalue, merge_depth - 1); + left_map.insert(rname, merged_value); + } + None => { + left_map.insert(rname, rvalue); + } } } + Value::Table(left_map) + } else { + Value::Table(right_map) } - Value::Table(left_map) } // Catch everything else we didn't handle, and use the right value (_, value) => value, @@ -187,7 +207,7 @@ mod merge_toml_tests { .expect("Couldn't parse built-in languages config"); let user: Value = toml::from_str(USER).unwrap(); - let merged = merge_toml_values(base, user, true); + let merged = merge_toml_values(base, user, 3); let languages = merged.get("language").unwrap().as_array().unwrap(); let nix = languages .iter() @@ -220,7 +240,7 @@ mod merge_toml_tests { .expect("Couldn't parse built-in languages config"); let user: Value = toml::from_str(USER).unwrap(); - let merged = merge_toml_values(base, user, true); + let merged = merge_toml_values(base, user, 3); let languages = merged.get("language").unwrap().as_array().unwrap(); let ts = languages .iter() diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 6a5f9d5ca4f7b..79d9609e1c5f7 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -58,7 +58,7 @@ pub enum OffsetEncoding { pub mod util { use super::*; - use helix_core::{Range, Rope, Transaction}; + use helix_core::{diagnostic::NumberOrString, Range, Rope, Transaction}; /// Converts a diagnostic in the document to [`lsp::Diagnostic`]. /// @@ -78,11 +78,19 @@ pub mod util { Error => lsp::DiagnosticSeverity::ERROR, }); + let code = match diag.code.clone() { + Some(x) => match x { + NumberOrString::Number(x) => Some(lsp::NumberOrString::Number(x)), + NumberOrString::String(x) => Some(lsp::NumberOrString::String(x)), + }, + None => None, + }; + // TODO: add support for Diagnostic.data lsp::Diagnostic::new( range_to_lsp_range(doc, range, offset_encoding), severity, - None, + code, None, diag.message.to_owned(), None, @@ -205,22 +213,6 @@ pub mod util { }), ) } - - /// The result of asking the language server to format the document. This can be turned into a - /// `Transaction`, but the advantage of not doing that straight away is that this one is - /// `Send` and `Sync`. - #[derive(Clone, Debug)] - pub struct LspFormatting { - pub doc: Rope, - pub edits: Vec, - pub offset_encoding: OffsetEncoding, - } - - impl From for Transaction { - fn from(fmt: LspFormatting) -> Transaction { - generate_transaction_from_edits(&fmt.doc, fmt.edits, fmt.offset_encoding) - } - } } #[derive(Debug, PartialEq, Clone)] diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index b0e22896a4ece..6724cabc48c06 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/helix-editor/helix" homepage = "https://helix-editor.com" include = ["src/**/*", "README.md"] default-run = "hx" +rust-version = "1.57" [package.metadata.nix] build = true @@ -41,7 +42,7 @@ crossterm = { version = "0.24", features = ["event-stream"] } signal-hook = "0.3" tokio-stream = "0.1" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } -arc-swap = { version = "1.5.0" } +arc-swap = { version = "1.5.1" } # Logging fern = "0.6" diff --git a/helix-term/build.rs b/helix-term/build.rs index 974f4b5ed7f0c..74c35a3a30a8c 100644 --- a/helix-term/build.rs +++ b/helix-term/build.rs @@ -19,7 +19,8 @@ fn main() { if std::env::var("HELIX_DISABLE_AUTO_GRAMMAR_BUILD").is_err() { fetch_grammars().expect("Failed to fetch tree-sitter grammars"); - build_grammars().expect("Failed to compile tree-sitter grammars"); + build_grammars(Some(std::env::var("TARGET").unwrap())) + .expect("Failed to compile tree-sitter grammars"); } println!("cargo:rerun-if-changed=../runtime/grammars/"); diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index a019188989f11..21be7db07c131 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -2,6 +2,7 @@ use arc_swap::{access::Map, ArcSwap}; use futures_util::Stream; use helix_core::{ config::{default_syntax_loader, user_syntax_loader}, + diagnostic::NumberOrString, pos_at_coords, syntax, Selection, }; use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap}; @@ -88,9 +89,8 @@ impl Application { use helix_view::editor::Action; - let config_dir = helix_loader::config_dir(); let theme_loader = std::sync::Arc::new(theme::Loader::new( - &config_dir, + &helix_loader::config_dir(), &helix_loader::runtime_dir(), )); @@ -555,12 +555,24 @@ impl Application { } }; + let code = match diagnostic.code.clone() { + Some(x) => match x { + lsp::NumberOrString::Number(x) => { + Some(NumberOrString::Number(x)) + } + lsp::NumberOrString::String(x) => { + Some(NumberOrString::String(x)) + } + }, + None => None, + }; + Some(Diagnostic { range: Range { start, end }, line: diagnostic.range.start.line as usize, message: diagnostic.message.clone(), severity, - // code + code, // source }) }) @@ -787,7 +799,7 @@ impl Application { fn restore_term(&mut self) -> Result<(), Error> { let mut stdout = stdout(); // reset cursor shape - write!(stdout, "\x1B[2 q")?; + write!(stdout, "\x1B[0 q")?; // Ignore errors on disabling, this might trigger on windows if we call // disable without calling enable previously let _ = execute!(stdout, DisableMouseCapture); diff --git a/helix-term/src/args.rs b/helix-term/src/args.rs index c3019ea7c875b..d16d7dfdf3d49 100644 --- a/helix-term/src/args.rs +++ b/helix-term/src/args.rs @@ -14,6 +14,7 @@ pub struct Args { pub build_grammars: bool, pub split: Option, pub verbosity: u64, + pub config_file: Option, pub files: Vec<(PathBuf, Position)>, } @@ -43,6 +44,10 @@ impl Args { anyhow::bail!("--grammar must be followed by either 'fetch' or 'build'") } }, + "-c" | "--config" => match argv.next().as_deref() { + Some(path) => args.config_file = Some(path.into()), + None => anyhow::bail!("--config must specify a path to read"), + }, arg if arg.starts_with("--") => { anyhow::bail!("unexpected double dash argument: {}", arg) } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e1326ba0789d1..17d908a8f19b2 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -28,7 +28,7 @@ use helix_core::{ }; use helix_view::{ clipboard::ClipboardType, - document::{Mode, SCRATCH_BUFFER_NAME}, + document::{FormatterError, Mode, SCRATCH_BUFFER_NAME}, editor::{Action, Motion}, info::Info, input::KeyEvent, @@ -204,17 +204,17 @@ impl MappableCommand { extend_line_down, "Extend down", copy_selection_on_next_line, "Copy selection on next line", copy_selection_on_prev_line, "Copy selection on previous line", - move_next_word_start, "Move to beginning of next word", - move_prev_word_start, "Move to beginning of previous word", + move_next_word_start, "Move to start of next word", + move_prev_word_start, "Move to start of previous word", move_prev_word_end, "Move to end of previous word", move_next_word_end, "Move to end of next word", - move_next_long_word_start, "Move to beginning of next long word", - move_prev_long_word_start, "Move to beginning of previous long word", + move_next_long_word_start, "Move to start of next long word", + move_prev_long_word_start, "Move to start of previous long word", move_next_long_word_end, "Move to end of next long word", - extend_next_word_start, "Extend to beginning of next word", - extend_prev_word_start, "Extend to beginning of previous word", - extend_next_long_word_start, "Extend to beginning of next long word", - extend_prev_long_word_start, "Extend to beginning of previous long word", + extend_next_word_start, "Extend to start of next word", + extend_prev_word_start, "Extend to start of previous word", + extend_next_long_word_start, "Extend to start of next long word", + extend_prev_long_word_start, "Extend to start of previous long word", extend_next_long_word_end, "Extend to end of next long word", extend_next_word_end, "Extend to end of next word", find_till_char, "Move till next occurrence of char", @@ -225,7 +225,7 @@ impl MappableCommand { find_prev_char, "Move to previous occurrence of char", extend_till_prev_char, "Extend till previous occurrence of char", extend_prev_char, "Extend to previous occurrence of char", - repeat_last_motion, "repeat last motion(extend_next_char, extend_till_char, find_next_char, find_till_char...)", + repeat_last_motion, "Repeat last motion", replace, "Replace with new char", switch_case, "Switch (toggle) case", switch_to_uppercase, "Switch to uppercase", @@ -236,7 +236,7 @@ impl MappableCommand { half_page_down, "Move half page down", select_all, "Select whole document", select_regex, "Select all regex matches inside selections", - split_selection, "Split selection into subselections on regex matches", + split_selection, "Split selections on regex matches", split_selection_on_newline, "Split selection on newlines", search, "Search for regex pattern", rsearch, "Reverse search for regex pattern", @@ -245,20 +245,20 @@ impl MappableCommand { extend_search_next, "Add next search match to selection", extend_search_prev, "Add previous search match to selection", search_selection, "Use current selection as search pattern", - global_search, "Global Search in workspace folder", + global_search, "Global search in workspace folder", extend_line, "Select current line, if already selected, extend to next line", extend_line_above, "Select current line, if already selected, extend to previous line", - extend_to_line_bounds, "Extend selection to line bounds (line-wise selection)", - shrink_to_line_bounds, "Shrink selection to line bounds (line-wise selection)", + extend_to_line_bounds, "Extend selection to line bounds", + shrink_to_line_bounds, "Shrink selection to line bounds", delete_selection, "Delete selection", - delete_selection_noyank, "Delete selection, without yanking", - change_selection, "Change selection (delete and enter insert mode)", - change_selection_noyank, "Change selection (delete and enter insert mode, without yanking)", - collapse_selection, "Collapse selection onto a single cursor", + delete_selection_noyank, "Delete selection without yanking", + change_selection, "Change selection", + change_selection_noyank, "Change selection without yanking", + collapse_selection, "Collapse selection into single cursor", flip_selections, "Flip selection cursor and anchor", - ensure_selections_forward, "Ensure the selection is in forward direction", + ensure_selections_forward, "Ensure all selections face forward", insert_mode, "Insert before selection", - append_mode, "Insert after selection (append)", + append_mode, "Append after selection", command_mode, "Enter command mode", file_picker, "Open file picker", file_picker_in_current_directory, "Open file picker at current working directory", @@ -272,7 +272,7 @@ impl MappableCommand { workspace_diagnostics_picker, "Open workspace diagnostic picker", last_picker, "Open last picker", prepend_to_line, "Insert at start of line", - append_to_line, "Insert at end of line", + append_to_line, "Append to end of line", open_below, "Open new line below selection", open_above, "Open new line above selection", normal_mode, "Enter normal mode", @@ -319,13 +319,13 @@ impl MappableCommand { delete_char_forward, "Delete next char", delete_word_backward, "Delete previous word", delete_word_forward, "Delete next word", - kill_to_line_start, "Delete content till the start of the line", - kill_to_line_end, "Delete content till the end of the line", + kill_to_line_start, "Delete till start of line", + kill_to_line_end, "Delete till end of line", undo, "Undo change", redo, "Redo change", earlier, "Move backward in history", later, "Move forward in history", - commit_undo_checkpoint, "Commit changes to a new checkpoint", + commit_undo_checkpoint, "Commit changes to new checkpoint", yank, "Yank selection", yank_joined_to_clipboard, "Join and yank selections to clipboard", yank_main_selection_to_clipboard, "Yank main selection to clipboard", @@ -333,7 +333,7 @@ impl MappableCommand { yank_main_selection_to_primary_clipboard, "Yank main selection to primary clipboard", replace_with_yanked, "Replace with yanked text", replace_selections_with_clipboard, "Replace selections by clipboard content", - replace_selections_with_primary_clipboard, "Replace selections by primary clipboard content", + replace_selections_with_primary_clipboard, "Replace selections by primary clipboard", paste_after, "Paste after selection", paste_before, "Paste before selection", paste_clipboard_after, "Paste clipboard after selections", @@ -358,19 +358,19 @@ impl MappableCommand { rotate_selection_contents_backward, "Rotate selections contents backward", expand_selection, "Expand selection to parent syntax node", shrink_selection, "Shrink selection to previously expanded syntax node", - select_next_sibling, "Select the next sibling in the syntax tree", - select_prev_sibling, "Select the previous sibling in the syntax tree", + select_next_sibling, "Select next sibling in syntax tree", + select_prev_sibling, "Select previous sibling in syntax tree", jump_forward, "Jump forward on jumplist", jump_backward, "Jump backward on jumplist", - save_selection, "Save the current selection to the jumplist", - jump_view_right, "Jump to the split to the right", - jump_view_left, "Jump to the split to the left", - jump_view_up, "Jump to the split above", - jump_view_down, "Jump to the split below", - swap_view_right, "Swap with the split to the right", - swap_view_left, "Swap with the split to the left", - swap_view_up, "Swap with the split above", - swap_view_down, "Swap with the split below", + save_selection, "Save current selection to jumplist", + jump_view_right, "Jump to right split", + jump_view_left, "Jump to left split", + jump_view_up, "Jump to split above", + jump_view_down, "Jump to split below", + swap_view_right, "Swap with right split", + swap_view_left, "Swap with left split", + swap_view_up, "Swap with split above", + swap_view_down, "Swap with split below", transpose_view, "Transpose splits", rotate_view, "Goto next window", hsplit, "Horizontal bottom split", @@ -378,7 +378,7 @@ impl MappableCommand { vsplit, "Vertical right split", vsplit_new, "Vertical right split scratch buffer", wclose, "Close window", - wonly, "Current window only", + wonly, "Close windows except current", select_register, "Select register", insert_register, "Insert register", align_view_middle, "Align view middle", @@ -414,21 +414,21 @@ impl MappableCommand { dap_next, "Step to next", dap_variables, "List variables", dap_terminate, "End debug session", - dap_edit_condition, "Edit condition of the breakpoint on the current line", - dap_edit_log, "Edit log message of the breakpoint on the current line", + dap_edit_condition, "Edit breakpoint condition on current line", + dap_edit_log, "Edit breakpoint log message on current line", dap_switch_thread, "Switch current thread", dap_switch_stack_frame, "Switch stack frame", dap_enable_exceptions, "Enable exception breakpoints", dap_disable_exceptions, "Disable exception breakpoints", shell_pipe, "Pipe selections through shell command", - shell_pipe_to, "Pipe selections into shell command, ignoring command output", - shell_insert_output, "Insert output of shell command before each selection", - shell_append_output, "Append output of shell command after each selection", + shell_pipe_to, "Pipe selections into shell command ignoring output", + shell_insert_output, "Insert shell command output before selections", + shell_append_output, "Append shell command output after selections", shell_keep_pipe, "Filter selections with shell predicate", - suspend, "Suspend", + suspend, "Suspend and return to shell", rename_symbol, "Rename symbol", - increment, "Increment", - decrement, "Decrement", + increment, "Increment item under cursor", + decrement, "Decrement item under cursor", record_macro, "Record macro", replay_macro, "Replay macro", command_palette, "Open command palette", @@ -766,7 +766,7 @@ fn trim_selections(cx: &mut Context) { .selection(view.id) .iter() .filter_map(|range| { - if range.is_empty() || range.fragment(text).chars().all(|ch| ch.is_whitespace()) { + if range.is_empty() || range.slice(text).chars().all(|ch| ch.is_whitespace()) { return None; } let mut start = range.from(); @@ -800,13 +800,14 @@ fn align_selections(cx: &mut Context) { let text = doc.text().slice(..); let selection = doc.selection(view.id); + let tab_width = doc.tab_width(); let mut column_widths: Vec> = Vec::new(); let mut last_line = text.len_lines() + 1; let mut col = 0; for range in selection { - let coords = coords_at_pos(text, range.head); - let anchor_coords = coords_at_pos(text, range.anchor); + let coords = visual_coords_at_pos(text, range.head, tab_width); + let anchor_coords = visual_coords_at_pos(text, range.anchor, tab_width); if coords.row != anchor_coords.row { cx.editor @@ -1288,12 +1289,12 @@ fn replace(cx: &mut Context) { fn switch_case_impl(cx: &mut Context, change_fn: F) where - F: Fn(Cow) -> Tendril, + F: Fn(RopeSlice) -> Tendril, { let (view, doc) = current!(cx.editor); let selection = doc.selection(view.id); let transaction = Transaction::change_by_selection(doc.text(), selection, |range| { - let text: Tendril = change_fn(range.fragment(doc.text().slice(..))); + let text: Tendril = change_fn(range.slice(doc.text().slice(..))); (range.from(), range.to(), Some(text)) }); @@ -1319,11 +1320,15 @@ fn switch_case(cx: &mut Context) { } fn switch_to_uppercase(cx: &mut Context) { - switch_case_impl(cx, |string| string.to_uppercase().into()); + switch_case_impl(cx, |string| { + string.chunks().map(|chunk| chunk.to_uppercase()).collect() + }); } fn switch_to_lowercase(cx: &mut Context) { - switch_case_impl(cx, |string| string.to_lowercase().into()); + switch_case_impl(cx, |string| { + string.chunks().map(|chunk| chunk.to_lowercase()).collect() + }); } pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) { @@ -1748,10 +1753,16 @@ fn extend_search_prev(cx: &mut Context) { fn search_selection(cx: &mut Context) { let (view, doc) = current!(cx.editor); let contents = doc.text().slice(..); - let query = doc.selection(view.id).primary().fragment(contents); - let regex = regex::escape(&query); + + let regex = doc + .selection(view.id) + .iter() + .map(|selection| regex::escape(&selection.fragment(contents))) + .collect::>() + .join("|"); + + let msg = format!("register '{}' set to '{}'", '/', ®ex); cx.editor.registers.get_mut('/').push(regex); - let msg = format!("register '{}' set to '{}'", '/', query); cx.editor.set_status(msg); } @@ -2464,14 +2475,14 @@ async fn make_format_callback( doc_id: DocumentId, doc_version: i32, modified: Modified, - format: impl Future + Send + 'static, + format: impl Future> + Send + 'static, ) -> anyhow::Result { - let format = format.await; + let format = format.await?; let call: job::Callback = Box::new(move |editor, _compositor| { let view_id = view!(editor).id; if let Some(doc) = editor.document_mut(doc_id) { if doc.version() == doc_version { - doc.apply(&Transaction::from(format), view_id); + doc.apply(&format, view_id); doc.append_changes_to_history(view_id); doc.detect_indent_and_line_ending(); if let Modified::SetUnmodified = modified { @@ -3896,8 +3907,8 @@ fn rotate_selection_contents(cx: &mut Context, direction: Direction) { let selection = doc.selection(view.id); let mut fragments: Vec<_> = selection - .fragments(text) - .map(|fragment| Tendril::from(fragment.as_ref())) + .slices(text) + .map(|fragment| fragment.chunks().collect()) .collect(); let group = count @@ -4503,8 +4514,8 @@ fn shell_keep_pipe(cx: &mut Context) { let text = doc.text().slice(..); for (i, range) in selection.ranges().iter().enumerate() { - let fragment = range.fragment(text); - let (_output, success) = match shell_impl(shell, input, Some(fragment.as_bytes())) { + let fragment = range.slice(text); + let (_output, success) = match shell_impl(shell, input, Some(fragment)) { Ok(result) => result, Err(err) => { cx.editor.set_error(err.to_string()); @@ -4535,20 +4546,24 @@ fn shell_keep_pipe(cx: &mut Context) { fn shell_impl( shell: &[String], cmd: &str, - input: Option<&[u8]>, + input: Option, ) -> anyhow::Result<(Tendril, bool)> { use std::io::Write; use std::process::{Command, Stdio}; ensure!(!shell.is_empty(), "No shell set"); - let mut process = match Command::new(&shell[0]) + let mut process = Command::new(&shell[0]); + process .args(&shell[1..]) .arg(cmd) - .stdin(Stdio::piped()) .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - { + .stderr(Stdio::piped()); + + if input.is_some() { + process.stdin(Stdio::piped()); + } + + let mut process = match process.spawn() { Ok(process) => process, Err(e) => { log::error!("Failed to start shell: {}", e); @@ -4557,7 +4572,9 @@ fn shell_impl( }; if let Some(input) = input { let mut stdin = process.stdin.take().unwrap(); - stdin.write_all(input)?; + for chunk in input.chunks() { + stdin.write_all(chunk.as_bytes())?; + } } let output = process.wait_with_output()?; @@ -4586,8 +4603,8 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) { let text = doc.text().slice(..); for range in selection.ranges() { - let fragment = range.fragment(text); - let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment.as_bytes())) { + let fragment = range.slice(text); + let (output, success) = match shell_impl(shell, cmd, pipe.then(|| fragment)) { Ok(result) => result, Err(err) => { cx.editor.set_error(err.to_string()); diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index 9f6f4c15cd325..1c780c1ff1e6d 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -216,6 +216,8 @@ pub fn dap_start_impl( } } + args.insert("cwd", to_value(std::env::current_dir().unwrap())?); + let args = to_value(args).unwrap(); let callback = |_editor: &mut Editor, _compositor: &mut Compositor, _response: Value| { diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 1785a50c29ddb..38507e4dd1cb3 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -904,7 +904,12 @@ pub fn signature_help_impl(cx: &mut Context, invoked: SignatureHelpInvoked) { Some((start, start + string.len())) } lsp::ParameterLabel::LabelOffsets([start, end]) => { - Some((*start as usize, *end as usize)) + // LS sends offsets based on utf-16 based string representation + // but highlighting in helix is done using byte offset. + use helix_core::str_utils::char_to_byte_idx; + let from = char_to_byte_idx(&signature.label, *start as usize); + let to = char_to_byte_idx(&signature.label, *end as usize); + Some((from, to)) } } }; diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index d6db117e67e4e..ad4e7f4ce9da6 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1254,6 +1254,7 @@ fn language( let doc = doc_mut!(cx.editor); doc.set_language_by_language_id(&args[0], cx.editor.syn_loader.clone()); + doc.detect_indent_and_line_ending(); let id = doc.id(); cx.editor.refresh_language_server(id); @@ -1291,8 +1292,8 @@ fn sort_impl( let selection = doc.selection(view.id); let mut fragments: Vec<_> = selection - .fragments(text) - .map(|fragment| Tendril::from(fragment.as_ref())) + .slices(text) + .map(|fragment| fragment.chunks().collect()) .collect(); fragments.sort_by(match reverse { @@ -1525,7 +1526,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit!", aliases: &["q!"], - doc: "Close the current view forcefully (ignoring unsaved changes).", + doc: "Force close the current view, ignoring unsaved changes.", fun: force_quit, completer: None, }, @@ -1546,7 +1547,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "buffer-close!", aliases: &["bc!", "bclose!"], - doc: "Close the current buffer forcefully (ignoring unsaved changes).", + doc: "Close the current buffer forcefully, ignoring unsaved changes.", fun: force_buffer_close, completer: Some(completers::buffer), }, @@ -1560,35 +1561,35 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "buffer-close-others!", aliases: &["bco!", "bcloseother!"], - doc: "Close all buffers but the currently focused one.", + doc: "Force close all buffers but the currently focused one.", fun: force_buffer_close_others, completer: None, }, TypableCommand { name: "buffer-close-all", aliases: &["bca", "bcloseall"], - doc: "Close all buffers, without quitting.", + doc: "Close all buffers without quitting.", fun: buffer_close_all, completer: None, }, TypableCommand { name: "buffer-close-all!", aliases: &["bca!", "bcloseall!"], - doc: "Close all buffers forcefully (ignoring unsaved changes), without quitting.", + doc: "Force close all buffers ignoring unsaved changes without quitting.", fun: force_buffer_close_all, completer: None, }, TypableCommand { name: "buffer-next", aliases: &["bn", "bnext"], - doc: "Go to next buffer.", + doc: "Goto next buffer.", fun: buffer_next, completer: None, }, TypableCommand { name: "buffer-previous", aliases: &["bp", "bprev"], - doc: "Go to previous buffer.", + doc: "Goto previous buffer.", fun: buffer_previous, completer: None, }, @@ -1602,7 +1603,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "write!", aliases: &["w!"], - doc: "Write changes to disk forcefully (creating necessary subdirectories). Accepts an optional path (:write some/path.txt)", + doc: "Force write changes to disk creating necessary subdirectories. Accepts an optional path (:write some/path.txt)", fun: force_write, completer: Some(completers::filename), }, @@ -1696,7 +1697,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit-all!", aliases: &["qa!"], - doc: "Close all views forcefully (ignoring unsaved changes).", + doc: "Force close all views ignoring unsaved changes.", fun: force_quit_all, completer: None, }, @@ -1710,7 +1711,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "cquit!", aliases: &["cq!"], - doc: "Quit with exit code (default 1) forcefully (ignoring unsaved changes). Accepts an optional integer exit code (:cq! 2).", + doc: "Force quit with exit code (default 1) ignoring unsaved changes. Accepts an optional integer exit code (:cq! 2).", fun: force_cquit, completer: None, }, @@ -1815,7 +1816,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "encoding", aliases: &[], - doc: "Set encoding based on `https://encoding.spec.whatwg.org`", + doc: "Set encoding. Based on `https://encoding.spec.whatwg.org`.", fun: set_encoding, completer: None, }, @@ -1892,7 +1893,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "goto", aliases: &["g"], - doc: "Go to line number.", + doc: "Goto line number.", fun: goto_line_number, completer: None, }, @@ -1948,14 +1949,14 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "config-reload", aliases: &[], - doc: "Refreshes helix's config.", + doc: "Refresh user config.", fun: refresh_config, completer: None, }, TypableCommand { name: "config-open", aliases: &[], - doc: "Open the helix config.toml file.", + doc: "Open the user config.toml file.", fun: open_config, completer: None, }, diff --git a/helix-term/src/health.rs b/helix-term/src/health.rs index f64e121d5fd17..6d49105a3e950 100644 --- a/helix-term/src/health.rs +++ b/helix-term/src/health.rs @@ -4,6 +4,7 @@ use crossterm::{ }; use helix_core::config::{default_syntax_loader, user_syntax_loader}; use helix_loader::grammar::load_runtime_file; +use helix_view::clipboard::get_clipboard_provider; use std::io::Write; #[derive(Copy, Clone)] @@ -52,6 +53,7 @@ pub fn general() -> std::io::Result<()> { let lang_file = helix_loader::lang_config_file(); let log_file = helix_loader::log_file(); let rt_dir = helix_loader::runtime_dir(); + let clipboard_provider = get_clipboard_provider(); if config_file.exists() { writeln!(stdout, "Config file: {}", config_file.display())?; @@ -76,6 +78,7 @@ pub fn general() -> std::io::Result<()> { if rt_dir.read_dir().ok().map(|it| it.count()) == Some(0) { writeln!(stdout, "{}", "Runtime directory is empty.".red())?; } + writeln!(stdout, "Clipboard provider: {}", clipboard_provider.name())?; Ok(()) } diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index f6fb6140b6d3a..8a16dc1be1b1e 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -358,9 +358,7 @@ pub fn default() -> HashMap { "left" => move_char_left, "C-b" => move_char_left, "down" => move_line_down, - "C-n" => move_line_down, "up" => move_line_up, - "C-p" => move_line_up, "right" => move_char_right, "C-f" => move_char_right, "A-b" => move_prev_word_end, diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index eb186d78e2acf..7f04f2014b957 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -64,6 +64,7 @@ FLAGS: --health [LANG] Checks for potential errors in editor setup If given, checks for config errors in language LANG -g, --grammar {{fetch|build}} Fetches or builds tree-sitter grammars listed in languages.toml + -c, --config Specifies a file to use for configuration -v Increases logging verbosity each use for up to 3 times (default file: {}) -V, --version Prints version information @@ -108,7 +109,7 @@ FLAGS: } if args.build_grammars { - helix_loader::grammar::build_grammars()?; + helix_loader::grammar::build_grammars(None)?; return Ok(0); } @@ -119,14 +120,15 @@ FLAGS: std::fs::create_dir_all(&config_dir).ok(); } - let config = match std::fs::read_to_string(config_dir.join("config.toml")) { + helix_loader::initialize_config_file(args.config_file.clone()); + + let config = match std::fs::read_to_string(helix_loader::config_file()) { Ok(config) => toml::from_str(&config) .map(helix_term::keymap::merge_keys) .unwrap_or_else(|err| { eprintln!("Bad config: {}", err); eprintln!("Press to continue with default config"); use std::io::Read; - // This waits for an enter press. let _ = std::io::stdin().read(&mut []); Config::default() }), diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index a5774132cd7ec..401d284eb667f 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -413,7 +413,13 @@ impl EditorView { let mut is_in_indent_area = true; let mut last_line_indent_level = 0; - let indent_style = theme.get("ui.virtual.indent-guide"); + + // use whitespace style as fallback for indent-guide + let indent_guide_style = text_style.patch( + theme + .try_get("ui.virtual.indent-guide") + .unwrap_or_else(|| theme.get("ui.virtual.whitespace")), + ); let draw_indent_guides = |indent_level, line, surface: &mut Surface| { if !config.indent_guides.render { @@ -429,7 +435,7 @@ impl EditorView { viewport.x + (i * tab_width as u16) - offset.col as u16, viewport.y + line, &indent_guide_char, - indent_style, + indent_guide_style, ); } }; @@ -486,14 +492,7 @@ impl EditorView { ); } - // This is an empty line; draw indent guides at previous line's - // indent level to avoid breaking the guides on blank lines. - if visual_x == 0 { - draw_indent_guides(last_line_indent_level, line, surface); - } else if is_in_indent_area { - // A line with whitespace only - draw_indent_guides(visual_x, line, surface); - } + draw_indent_guides(last_line_indent_level, line, surface); visual_x = 0; line += 1; @@ -529,6 +528,8 @@ impl EditorView { (grapheme.as_ref(), width) }; + let cut_off_start = offset.col.saturating_sub(visual_x as usize); + if !out_of_bounds { // if we're offscreen just keep going until we hit a new line surface.set_string( @@ -541,7 +542,24 @@ impl EditorView { style }, ); + } else if cut_off_start != 0 && cut_off_start < width { + // partially on screen + let rect = Rect::new( + viewport.x as u16, + viewport.y + line, + (width - cut_off_start) as u16, + 1, + ); + surface.set_style( + rect, + if is_whitespace { + style.patch(whitespace_style) + } else { + style + }, + ); } + if is_in_indent_area && !(grapheme == " " || grapheme == "\t") { draw_indent_guides(visual_x, line, surface); is_in_indent_area = false; @@ -1010,7 +1028,7 @@ impl EditorView { None => return EventResult::Ignored(None), } - let offset = config.scroll_lines.abs() as usize; + let offset = config.scroll_lines.unsigned_abs(); commands::scroll(cxt, offset, direction); cxt.editor.tree.focus = current_view; @@ -1028,8 +1046,8 @@ impl EditorView { if doc .selection(view.id) .primary() - .fragment(doc.text().slice(..)) - .width() + .slice(doc.text().slice(..)) + .len_chars() <= 1 { return EventResult::Ignored(None); diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 257608f00dac3..113bc4864ccba 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -283,14 +283,28 @@ pub mod completers { names } + /// Recursive function to get all keys from this value and add them to vec + fn get_keys(value: &serde_json::Value, vec: &mut Vec, scope: Option<&str>) { + if let Some(map) = value.as_object() { + for (key, value) in map.iter() { + let key = match scope { + Some(scope) => format!("{}.{}", scope, key), + None => key.clone(), + }; + get_keys(value, vec, Some(&key)); + if !value.is_object() { + vec.push(key); + } + } + } + } + pub fn setting(_editor: &Editor, input: &str) -> Vec { static KEYS: Lazy> = Lazy::new(|| { - serde_json::json!(Config::default()) - .as_object() - .unwrap() - .keys() - .cloned() - .collect() + let mut keys = Vec::new(); + let json = serde_json::json!(Config::default()); + get_keys(&json, &mut keys, None); + keys }); let matcher = Matcher::default(); diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 7f89f1ca6afc0..4cb38fb0bfa93 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -532,16 +532,17 @@ impl Component for Prompt { .map(|entry| entry.into()) .unwrap_or_else(|| Cow::from("")) } else { + if let Some(register) = self.history_register { + // store in history + let register = cx.editor.registers.get_mut(register); + register.push(self.line.clone()); + } + self.line.as_str().into() }; (self.callback_fn)(cx, &input, PromptEvent::Validate); - if let Some(register) = self.history_register { - // store in history - let register = cx.editor.registers.get_mut(register); - register.push(self.line.clone()); - } return close_fn; } } diff --git a/helix-term/src/ui/statusline.rs b/helix-term/src/ui/statusline.rs index 85992c6021643..75e5dbd7bb433 100644 --- a/helix-term/src/ui/statusline.rs +++ b/helix-term/src/ui/statusline.rs @@ -1,4 +1,4 @@ -use helix_core::{coords_at_pos, encoding}; +use helix_core::{coords_at_pos, encoding, Position}; use helix_view::{ document::{Mode, SCRATCH_BUFFER_NAME}, graphics::Rect, @@ -143,6 +143,9 @@ where helix_view::editor::StatusLineElement::Diagnostics => render_diagnostics, helix_view::editor::StatusLineElement::Selections => render_selections, helix_view::editor::StatusLineElement::Position => render_position, + helix_view::editor::StatusLineElement::PositionPercentage => render_position_percentage, + helix_view::editor::StatusLineElement::Separator => render_separator, + helix_view::editor::StatusLineElement::Spacer => render_spacer, } } @@ -250,19 +253,22 @@ where ); } -fn render_position(context: &mut RenderContext, write: F) -where - F: Fn(&mut RenderContext, String, Option