Merge branch 'helix-editor:master' into pull-diagnostics

This commit is contained in:
SofusA 2024-12-14 16:36:41 +01:00 committed by GitHub
commit 1c36972c79
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
77 changed files with 1581 additions and 554 deletions

View file

@ -8,6 +8,9 @@ on:
schedule:
- cron: "00 01 * * *"
env:
MSRV: "1.76"
jobs:
check:
name: Check (msrv)
@ -18,7 +21,9 @@ jobs:
uses: actions/checkout@v4
- name: Install MSRV toolchain
uses: dtolnay/rust-toolchain@1.76
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MSRV }}
- uses: Swatinem/rust-cache@v2
with:
@ -39,7 +44,9 @@ jobs:
uses: actions/checkout@v4
- name: Install MSRV toolchain
uses: dtolnay/rust-toolchain@1.76
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MSRV }}
- uses: Swatinem/rust-cache@v2
with:
@ -71,8 +78,9 @@ jobs:
uses: actions/checkout@v4
- name: Install MSRV toolchain
uses: dtolnay/rust-toolchain@1.76
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MSRV }}
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
@ -99,7 +107,9 @@ jobs:
uses: actions/checkout@v4
- name: Install MSRV toolchain
uses: dtolnay/rust-toolchain@1.76
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MSRV }}
- uses: Swatinem/rust-cache@v2
with:

View file

@ -16,8 +16,8 @@ jobs:
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v2
with:
mdbook-version: 'latest'
# mdbook-version: '0.4.8'
# mdbook-version: 'latest'
mdbook-version: '0.4.43'
- run: mdbook build book

232
Cargo.lock generated
View file

@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cc"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc"
dependencies = [
"shlex",
]
@ -479,9 +479,9 @@ checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "gix"
version = "0.67.0"
version = "0.68.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d3e78ddac368d3e3bfbc2862bc2aafa3d89f1b15fed898d9761e1ec6f3f17f"
checksum = "b04c66359b5e17f92395abc433861df0edf48f39f3f590818d1d7217327dd6a1"
dependencies = [
"gix-actor",
"gix-attributes",
@ -522,28 +522,28 @@ dependencies = [
"gix-worktree",
"once_cell",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-actor"
version = "0.33.0"
version = "0.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59226ef06661c756e664b46b1d3b2c198f6adc5407a484c086d0171108a70027"
checksum = "32b24171f514cef7bb4dfb72a0b06dacf609b33ba8ad2489d4c4559a03b7afb3"
dependencies = [
"bstr",
"gix-date",
"gix-utils",
"itoa",
"thiserror 1.0.69",
"thiserror 2.0.3",
"winnow",
]
[[package]]
name = "gix-attributes"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31a102d201ef0e5a848458a82292581e7641e52f0f52e693b6cbdd05a652c029"
checksum = "ddf9bf852194c0edfe699a2d36422d2c1f28f73b7c6d446c3f0ccd3ba232cadc"
dependencies = [
"bstr",
"gix-glob",
@ -552,33 +552,33 @@ dependencies = [
"gix-trace",
"kstring",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
"unicode-bom",
]
[[package]]
name = "gix-bitmap"
version = "0.2.12"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f78312288bd02052be5dbc2ecbc342c9f4eb791986d86c0a5c06b92dc72efa"
checksum = "d48b897b4bbc881aea994b4a5bbb340a04979d7be9089791304e04a9fbc66b53"
dependencies = [
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-chunk"
version = "0.4.9"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28b58ba04f0c004722344390af9dbc85888fbb84be1981afb934da4114d4cf"
checksum = "c6ffbeb3a5c0b8b84c3fe4133a6f8c82fa962f4caefe8d0762eced025d3eb4f7"
dependencies = [
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-command"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c201d2b9e9cce2365a6638fd0a966f751ed92d74be5c0727ac331e6a29ef5846"
checksum = "6d7d6b8f3a64453fd7e8191eb80b351eb7ac0839b40a1237cd2c137d5079fe53"
dependencies = [
"bstr",
"gix-path",
@ -588,23 +588,23 @@ dependencies = [
[[package]]
name = "gix-commitgraph"
version = "0.25.0"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41db900b189e62dc61575f06fdf1a3b6901d264a99be9d32b286af6b2e3984e1"
checksum = "a8da6591a7868fb2b6dabddea6b09988b0b05e0213f938dbaa11a03dd7a48d85"
dependencies = [
"bstr",
"gix-chunk",
"gix-features",
"gix-hash",
"memmap2",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-config"
version = "0.41.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bedd1bf1c7b994be9d57207e8e0de79016c05e2e8701d3015da906e65ac445e"
checksum = "6649b406ca1f99cb148959cf00468b231f07950f8ec438cc0903cda563606f19"
dependencies = [
"bstr",
"gix-config-value",
@ -616,41 +616,41 @@ dependencies = [
"memchr",
"once_cell",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
"unicode-bom",
"winnow",
]
[[package]]
name = "gix-config-value"
version = "0.14.9"
version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3de3fdca9c75fa4b83a76583d265fa49b1de6b088ebcd210749c24ceeb74660"
checksum = "49aaeef5d98390a3bcf9dbc6440b520b793d1bf3ed99317dc407b02be995b28e"
dependencies = [
"bitflags",
"bstr",
"gix-path",
"libc",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-date"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d10d543ac13c97292a15e8e8b7889cd006faf739777437ed95362504b8fe81a0"
checksum = "691142b1a34d18e8ed6e6114bc1a2736516c5ad60ef3aa9bd1b694886e3ca92d"
dependencies = [
"bstr",
"itoa",
"jiff",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-diff"
version = "0.47.0"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9850fd0c15af113db6f9e130d13091ba0d3754e570a2afdff9e2f3043da260e"
checksum = "a327be31a392144b60ab0b1c863362c32a1c8f7effdfa2141d5d5b6b916ef3bf"
dependencies = [
"bstr",
"gix-command",
@ -664,14 +664,14 @@ dependencies = [
"gix-traverse",
"gix-worktree",
"imara-diff",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-dir"
version = "0.9.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbf6c29bf17baf3996d4925fad5e10c1a12eac9b3a0d8475d89292e0e5ba34a3"
checksum = "acd6a0618958f9cce78a32724f8e06c4f4a57ca7080f645736d53676dc9b4db9"
dependencies = [
"bstr",
"gix-discover",
@ -684,14 +684,14 @@ dependencies = [
"gix-trace",
"gix-utils",
"gix-worktree",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-discover"
version = "0.36.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c522e31f458f50af09dfb014e10873c5378f702f8049c96f508989aad59671f6"
checksum = "83bf6dfa4e266a4a9becb4d18fc801f92c3f7cc6c433dd86fdadbcf315ffb6ef"
dependencies = [
"bstr",
"dunce",
@ -700,14 +700,14 @@ dependencies = [
"gix-path",
"gix-ref",
"gix-sec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-features"
version = "0.39.0"
version = "0.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e0eb9efdf96c35c0bed7596d1bef2d4ce6360a1d09738001f9d3e402aa7ba3e"
checksum = "7d85d673f2e022a340dba4713bed77ef2cf4cd737d2f3e0f159d45e0935fd81f"
dependencies = [
"crc32fast",
"flate2",
@ -718,15 +718,15 @@ dependencies = [
"once_cell",
"prodash",
"sha1_smol",
"thiserror 1.0.69",
"thiserror 2.0.3",
"walkdir",
]
[[package]]
name = "gix-filter"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b37f82359a4485770ed8993ae715ced1bf674f2a63e45f5a0786d38310665ea"
checksum = "5108cc58d58b27df10ac4de7f31b2eb96d588a33e5eba23739b865f5d8db7995"
dependencies = [
"bstr",
"encoding_rs",
@ -740,7 +740,7 @@ dependencies = [
"gix-trace",
"gix-utils",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
@ -756,9 +756,9 @@ dependencies = [
[[package]]
name = "gix-glob"
version = "0.17.0"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "254b5101cf7facc00d9b5ff564cf46302ca76695cca23d33bc958a707b6fc857"
checksum = "aaf69a6bec0a3581567484bf99a4003afcaf6c469fd4214352517ea355cf3435"
dependencies = [
"bitflags",
"bstr",
@ -768,12 +768,12 @@ dependencies = [
[[package]]
name = "gix-hash"
version = "0.15.0"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "952c3a29f1bc1007cc901abce7479943abfa42016db089de33d0a4fa3c85bfe8"
checksum = "0b5eccc17194ed0e67d49285e4853307e4147e95407f91c1c3e4a13ba9f4e4ce"
dependencies = [
"faster-hex",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
@ -789,9 +789,9 @@ dependencies = [
[[package]]
name = "gix-ignore"
version = "0.12.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba55a9b582dc26a639875497615959a8127ac5c37b2426dc50f037fada33a4b7"
checksum = "b6b1fb24d2a4af0aa7438e2771d60c14a80cf2c9bd55c29cf1712b841f05bb8a"
dependencies = [
"bstr",
"gix-glob",
@ -802,9 +802,9 @@ dependencies = [
[[package]]
name = "gix-index"
version = "0.36.0"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27619009ca1ea33fd885041273f5fa5a09163a5c1d22a913b28d7b985e66fe29"
checksum = "270645fd20556b64c8ffa1540d921b281e6994413a0ca068596f97e9367a257a"
dependencies = [
"bitflags",
"bstr",
@ -825,7 +825,7 @@ dependencies = [
"memmap2",
"rustix",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
@ -841,9 +841,9 @@ dependencies = [
[[package]]
name = "gix-object"
version = "0.45.0"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a77b6e7753d298553d9ae8b1744924481e7a49170983938bb578dccfbc6fc1a"
checksum = "65d93e2bbfa83a307e47f45e45de7b6c04d7375a8bd5907b215f4bf45237d879"
dependencies = [
"bstr",
"gix-actor",
@ -855,15 +855,15 @@ dependencies = [
"gix-validate",
"itoa",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
"winnow",
]
[[package]]
name = "gix-odb"
version = "0.64.0"
version = "0.65.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb86aadf7f1b2f980601b4fc94309706f9700f8008f935dc512d556c9e60f61"
checksum = "93bed6e1b577c25a6bb8e6ecbf4df525f29a671ddf5f2221821a56a8dbeec4e3"
dependencies = [
"arc-swap",
"gix-date",
@ -877,14 +877,14 @@ dependencies = [
"gix-quote",
"parking_lot",
"tempfile",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-pack"
version = "0.54.0"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "363e6e59a855ba243672408139db68e2478126cdcfeabb420777df4a1f20026b"
checksum = "9b91fec04d359544fecbb8e85117ec746fbaa9046ebafcefb58cb74f20dc76d4"
dependencies = [
"clru",
"gix-chunk",
@ -895,39 +895,39 @@ dependencies = [
"gix-path",
"memmap2",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-packetline-blocking"
version = "0.18.0"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "decace940e8ba8e29d29b73b843a6cbae67503887f3e5fb7e688d0f4f6ee0757"
checksum = "ce9004ce1bc00fd538b11c1ec8141a1558fb3af3d2b7ac1ac5c41881f9e42d2a"
dependencies = [
"bstr",
"faster-hex",
"gix-trace",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-path"
version = "0.10.12"
version = "0.10.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c04e5a94fdb56b1e91eb7df2658ad16832428b8eeda24ff1a0f0288de2bce554"
checksum = "afc292ef1a51e340aeb0e720800338c805975724c1dfbd243185452efd8645b7"
dependencies = [
"bstr",
"gix-trace",
"home",
"once_cell",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-pathspec"
version = "0.8.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70f02bf7625dbf15bf9fedbeace2ac1ce1c5177806bdbc24c441d664c75c00e4"
checksum = "4c472dfbe4a4e96fcf7efddcd4771c9037bb4fdea2faaabf2f4888210c75b81e"
dependencies = [
"bitflags",
"bstr",
@ -935,25 +935,25 @@ dependencies = [
"gix-config-value",
"gix-glob",
"gix-path",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-quote"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f89f9a1525dcfd9639e282ea939f5ab0d09d93cf2b90c1fc6104f1b9582a8e49"
checksum = "64a1e282216ec2ab2816cd57e6ed88f8009e634aec47562883c05ac8a7009a63"
dependencies = [
"bstr",
"gix-utils",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-ref"
version = "0.48.0"
version = "0.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47385e71fa2d9da8c35e642ef4648808ddf0a52bc93425879088c706dfeaea2"
checksum = "1eae462723686272a58f49501015ef7c0d67c3e042c20049d8dd9c7eff92efde"
dependencies = [
"gix-actor",
"gix-features",
@ -966,29 +966,29 @@ dependencies = [
"gix-utils",
"gix-validate",
"memmap2",
"thiserror 1.0.69",
"thiserror 2.0.3",
"winnow",
]
[[package]]
name = "gix-refspec"
version = "0.26.0"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0022038a09d80d9abf773be8efcbb502868d97f6972b8633bfb52ab6edaac442"
checksum = "00c056bb747868c7eb0aeb352c9f9181ab8ca3d0a2550f16470803500c6c413d"
dependencies = [
"bstr",
"gix-hash",
"gix-revision",
"gix-validate",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-revision"
version = "0.30.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee8eb4088fece3562af4a5d751e069f90e93345524ad730512185234c4b55f1"
checksum = "44488e0380847967bc3e3cacd8b22652e02ea1eb58afb60edd91847695cd2d8d"
dependencies = [
"bstr",
"gix-commitgraph",
@ -996,14 +996,14 @@ dependencies = [
"gix-hash",
"gix-object",
"gix-revwalk",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-revwalk"
version = "0.16.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c9a9496da98d36ff19063a8576bf09a87425583b709a56dc5594fffa9d39b2"
checksum = "510026fc32f456f8f067d8f37c34088b97a36b2229d88a6a5023ef179fcb109d"
dependencies = [
"gix-commitgraph",
"gix-date",
@ -1011,14 +1011,14 @@ dependencies = [
"gix-hashtable",
"gix-object",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-sec"
version = "0.10.9"
version = "0.10.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2007538eda296445c07949cf04f4a767307d887184d6b3e83e2d636533ddc6e"
checksum = "a8b876ef997a955397809a2ec398d6a45b7a55b4918f2446344330f778d14fd6"
dependencies = [
"bitflags",
"gix-path",
@ -1028,9 +1028,9 @@ dependencies = [
[[package]]
name = "gix-status"
version = "0.14.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57414886e750161b4c86d8bca6b2d15bcc87f37ddc46684bb05cebbd29390543"
checksum = "201396192ee4c4dd9e8a84fed4b0d2b33d639fca815fb99b0f653dfeddf38585"
dependencies = [
"bstr",
"filetime",
@ -1046,14 +1046,14 @@ dependencies = [
"gix-pathspec",
"gix-worktree",
"portable-atomic",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-submodule"
version = "0.15.0"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed099621873cd36c580fc822176a32a7e50fef15a5c2ed81aaa087296f0497a"
checksum = "a2455f8c0fcb6ebe2a6e83c8f522d30615d763eb2ef7a23c7d929f9476e89f5c"
dependencies = [
"bstr",
"gix-config",
@ -1061,7 +1061,7 @@ dependencies = [
"gix-pathspec",
"gix-refspec",
"gix-url",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
@ -1086,9 +1086,9 @@ checksum = "04bdde120c29f1fc23a24d3e115aeeea3d60d8e65bab92cc5f9d90d9302eb952"
[[package]]
name = "gix-traverse"
version = "0.42.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f20f1b13cc4fa6ba92b24e6aa0c2fb6a34beb4458ef88c6300212db504e818df"
checksum = "3ff2ec9f779680f795363db1c563168b32b8d6728ec58564c628e85c92d29faf"
dependencies = [
"bitflags",
"gix-commitgraph",
@ -1098,19 +1098,19 @@ dependencies = [
"gix-object",
"gix-revwalk",
"smallvec",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-url"
version = "0.28.0"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33e7c297c3265015c133a2c02199610b6e1373a09dc4be057d0c1b5285737f06"
checksum = "e09f97db3618fb8e473d7d97e77296b50aaee0ddcd6a867f07443e3e87391099"
dependencies = [
"bstr",
"gix-features",
"gix-path",
"thiserror 1.0.69",
"thiserror 2.0.3",
"url",
]
@ -1127,19 +1127,19 @@ dependencies = [
[[package]]
name = "gix-validate"
version = "0.9.1"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e187b263461bc36cea17650141567753bc6207d036cedd1de6e81a52f277ff68"
checksum = "cd520d09f9f585b34b32aba1d0b36ada89ab7fefb54a8ca3fe37fc482a750937"
dependencies = [
"bstr",
"thiserror 1.0.69",
"thiserror 2.0.3",
]
[[package]]
name = "gix-worktree"
version = "0.37.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d345e5b523550fe4fa0e912bf957de752011ccfc87451968fda1b624318f29c"
checksum = "756dbbe15188fa22540d5eab941f8f9cf511a5364d5aec34c88083c09f4bea13"
dependencies = [
"bstr",
"gix-attributes",
@ -1757,15 +1757,15 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.164"
version = "0.2.167"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f"
checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
[[package]]
name = "libloading"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
@ -2084,9 +2084,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.8"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@ -2612,9 +2612,9 @@ checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
[[package]]
name = "url"
version = "2.5.3"
version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",

View file

@ -1,5 +1,16 @@
# Commands
Command mode can be activated by pressing `:`. The built-in commands are:
- [Typable commands](#typable-commands)
- [Static commands](#static-commands)
## Typable commands
Typable commands are used from command mode and may take arguments. Command mode can be activated by pressing `:`. The built-in typable commands are:
{{#include ./generated/typable-cmd.md}}
## Static Commands
Static commands take no arguments and can be bound to keys. Static commands can also be executed from the command picker (`<space>?`). The built-in static commands are:
{{#include ./generated/static-cmd.md}}

View file

@ -31,6 +31,7 @@
| `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` |
| `cursorcolumn` | Highlight all columns with a cursor | `false` |
| `continue-comments` | if helix should automatically add a line comment token if you create a new line inside a comment. | `true` |
| `gutters` | Gutters to display: Available are `diagnostics` and `diff` 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", "spacer", "line-numbers", "spacer", "diff"]` |
| `auto-completion` | Enable automatic pop up of auto-completion | `true` |
| `path-completion` | Enable filepath completion. Show files and directories if an existing path at the cursor was recognized, either absolute or relative to the current opened document or current working directory (if the buffer is not yet saved). Defaults to true. | `true` |
@ -58,7 +59,7 @@
### `[editor.clipboard-provider]` Section
Helix can be configured wither to use a builtin clipboard configuration or to use
Helix can be configured either to use a builtin clipboard configuration or to use
a provided command.
For instance, setting it to use OSC 52 termcodes, the configuration would be:
@ -151,7 +152,7 @@ The following statusline elements can be configured:
[^1]: By default, a progress spinner is shown in the statusline beside the file path.
[^2]: You may also have to activate them in the LSP config for them to appear, not just in Helix. Inlay hints in Helix are still being improved on and may be a little bit laggy/janky under some circumstances. Please report any bugs you see so we can fix them!
[^2]: You may also have to activate them in the language server config for them to appear, not just in Helix. Inlay hints in Helix are still being improved on and may be a little bit laggy/janky under some circumstances. Please report any bugs you see so we can fix them!
### `[editor.cursor-shape]` Section

View file

@ -1,4 +1,4 @@
| Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP |
| Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default language servers |
| --- | --- | --- | --- | --- |
| ada | ✓ | ✓ | | `ada_language_server` |
| adl | ✓ | ✓ | ✓ | |
@ -78,6 +78,7 @@
| gomod | ✓ | | | `gopls` |
| gotmpl | ✓ | | | `gopls` |
| gowork | ✓ | | | `gopls` |
| gpr | ✓ | | | `ada_language_server` |
| graphql | ✓ | ✓ | | `graphql-lsp` |
| groovy | ✓ | | | |
| gts | ✓ | ✓ | ✓ | `typescript-language-server`, `vscode-eslint-language-server`, `ember-language-server` |
@ -133,7 +134,7 @@
| mermaid | ✓ | | | |
| meson | ✓ | | ✓ | `mesonlsp` |
| mint | | | | `mint` |
| mojo | ✓ | ✓ | ✓ | `mojo-lsp-server` |
| mojo | ✓ | ✓ | ✓ | `magic` |
| move | ✓ | | | |
| msbuild | ✓ | | ✓ | |
| nasm | ✓ | ✓ | | |
@ -207,6 +208,7 @@
| tact | ✓ | ✓ | ✓ | |
| task | ✓ | | | |
| tcl | ✓ | | ✓ | |
| teal | ✓ | | | |
| templ | ✓ | | | `templ` |
| textproto | ✓ | ✓ | ✓ | |
| tfvars | ✓ | | ✓ | `terraform-ls` |
@ -224,6 +226,7 @@
| uxntal | ✓ | | | |
| v | ✓ | ✓ | ✓ | `v-analyzer` |
| vala | ✓ | ✓ | | `vala-language-server` |
| vento | ✓ | | | |
| verilog | ✓ | ✓ | | `svlangserver` |
| vhdl | ✓ | | | `vhdl_ls` |
| vhs | ✓ | | | |

View file

@ -0,0 +1,294 @@
| Name | Description | Default keybinds |
| --- | --- | --- |
| `no_op` | Do nothing | |
| `move_char_left` | Move left | normal: `` h ``, `` <left> ``, insert: `` <left> `` |
| `move_char_right` | Move right | normal: `` l ``, `` <right> ``, insert: `` <right> `` |
| `move_line_up` | Move up | normal: `` gk `` |
| `move_line_down` | Move down | normal: `` gj `` |
| `move_visual_line_up` | Move up | normal: `` k ``, `` <up> ``, insert: `` <up> `` |
| `move_visual_line_down` | Move down | normal: `` j ``, `` <down> ``, insert: `` <down> `` |
| `extend_char_left` | Extend left | select: `` h ``, `` <left> `` |
| `extend_char_right` | Extend right | select: `` l ``, `` <right> `` |
| `extend_line_up` | Extend up | select: `` gk `` |
| `extend_line_down` | Extend down | select: `` gj `` |
| `extend_visual_line_up` | Extend up | select: `` k ``, `` <up> `` |
| `extend_visual_line_down` | Extend down | select: `` j ``, `` <down> `` |
| `copy_selection_on_next_line` | Copy selection on next line | normal: `` C ``, select: `` C `` |
| `copy_selection_on_prev_line` | Copy selection on previous line | normal: `` <A-C> ``, select: `` <A-C> `` |
| `move_next_word_start` | Move to start of next word | normal: `` w `` |
| `move_prev_word_start` | Move to start of previous word | normal: `` b `` |
| `move_next_word_end` | Move to end of next word | normal: `` e `` |
| `move_prev_word_end` | Move to end of previous word | |
| `move_next_long_word_start` | Move to start of next long word | normal: `` W `` |
| `move_prev_long_word_start` | Move to start of previous long word | normal: `` B `` |
| `move_next_long_word_end` | Move to end of next long word | normal: `` E `` |
| `move_prev_long_word_end` | Move to end of previous long word | |
| `move_next_sub_word_start` | Move to start of next sub word | |
| `move_prev_sub_word_start` | Move to start of previous sub word | |
| `move_next_sub_word_end` | Move to end of next sub word | |
| `move_prev_sub_word_end` | Move to end of previous sub word | |
| `move_parent_node_end` | Move to end of the parent node | normal: `` <A-e> `` |
| `move_parent_node_start` | Move to beginning of the parent node | normal: `` <A-b> `` |
| `extend_next_word_start` | Extend to start of next word | select: `` w `` |
| `extend_prev_word_start` | Extend to start of previous word | select: `` b `` |
| `extend_next_word_end` | Extend to end of next word | select: `` e `` |
| `extend_prev_word_end` | Extend to end of previous word | |
| `extend_next_long_word_start` | Extend to start of next long word | select: `` W `` |
| `extend_prev_long_word_start` | Extend to start of previous long word | select: `` B `` |
| `extend_next_long_word_end` | Extend to end of next long word | select: `` E `` |
| `extend_prev_long_word_end` | Extend to end of prev long word | |
| `extend_next_sub_word_start` | Extend to start of next sub word | |
| `extend_prev_sub_word_start` | Extend to start of previous sub word | |
| `extend_next_sub_word_end` | Extend to end of next sub word | |
| `extend_prev_sub_word_end` | Extend to end of prev sub word | |
| `extend_parent_node_end` | Extend to end of the parent node | select: `` <A-e> `` |
| `extend_parent_node_start` | Extend to beginning of the parent node | select: `` <A-b> `` |
| `find_till_char` | Move till next occurrence of char | normal: `` t `` |
| `find_next_char` | Move to next occurrence of char | normal: `` f `` |
| `extend_till_char` | Extend till next occurrence of char | select: `` t `` |
| `extend_next_char` | Extend to next occurrence of char | select: `` f `` |
| `till_prev_char` | Move till previous occurrence of char | normal: `` T `` |
| `find_prev_char` | Move to previous occurrence of char | normal: `` F `` |
| `extend_till_prev_char` | Extend till previous occurrence of char | select: `` T `` |
| `extend_prev_char` | Extend to previous occurrence of char | select: `` F `` |
| `repeat_last_motion` | Repeat last motion | normal: `` <A-.> ``, select: `` <A-.> `` |
| `replace` | Replace with new char | normal: `` r ``, select: `` r `` |
| `switch_case` | Switch (toggle) case | normal: `` ~ ``, select: `` ~ `` |
| `switch_to_uppercase` | Switch to uppercase | normal: `` <A-`> ``, select: `` <A-`> `` |
| `switch_to_lowercase` | Switch to lowercase | normal: `` ` ``, select: `` ` `` |
| `page_up` | Move page up | normal: `` <C-b> ``, `` Z<C-b> ``, `` z<C-b> ``, `` <pageup> ``, `` Z<pageup> ``, `` z<pageup> ``, select: `` <C-b> ``, `` Z<C-b> ``, `` z<C-b> ``, `` <pageup> ``, `` Z<pageup> ``, `` z<pageup> ``, insert: `` <pageup> `` |
| `page_down` | Move page down | normal: `` <C-f> ``, `` Z<C-f> ``, `` z<C-f> ``, `` <pagedown> ``, `` Z<pagedown> ``, `` z<pagedown> ``, select: `` <C-f> ``, `` Z<C-f> ``, `` z<C-f> ``, `` <pagedown> ``, `` Z<pagedown> ``, `` z<pagedown> ``, insert: `` <pagedown> `` |
| `half_page_up` | Move half page up | |
| `half_page_down` | Move half page down | |
| `page_cursor_up` | Move page and cursor up | |
| `page_cursor_down` | Move page and cursor down | |
| `page_cursor_half_up` | Move page and cursor half up | normal: `` <C-u> ``, `` Z<C-u> ``, `` z<C-u> ``, `` Z<backspace> ``, `` z<backspace> ``, select: `` <C-u> ``, `` Z<C-u> ``, `` z<C-u> ``, `` Z<backspace> ``, `` z<backspace> `` |
| `page_cursor_half_down` | Move page and cursor half down | normal: `` <C-d> ``, `` Z<C-d> ``, `` z<C-d> ``, `` Z<space> ``, `` z<space> ``, select: `` <C-d> ``, `` Z<C-d> ``, `` z<C-d> ``, `` Z<space> ``, `` z<space> `` |
| `select_all` | Select whole document | normal: `` % ``, select: `` % `` |
| `select_regex` | Select all regex matches inside selections | normal: `` s ``, select: `` s `` |
| `split_selection` | Split selections on regex matches | normal: `` S ``, select: `` S `` |
| `split_selection_on_newline` | Split selection on newlines | normal: `` <A-s> ``, select: `` <A-s> `` |
| `merge_selections` | Merge selections | normal: `` <A-minus> ``, select: `` <A-minus> `` |
| `merge_consecutive_selections` | Merge consecutive selections | normal: `` <A-_> ``, select: `` <A-_> `` |
| `search` | Search for regex pattern | normal: `` / ``, `` Z/ ``, `` z/ ``, select: `` / ``, `` Z/ ``, `` z/ `` |
| `rsearch` | Reverse search for regex pattern | normal: `` ? ``, `` Z? ``, `` z? ``, select: `` ? ``, `` Z? ``, `` z? `` |
| `search_next` | Select next search match | normal: `` n ``, `` Zn ``, `` zn ``, select: `` Zn ``, `` zn `` |
| `search_prev` | Select previous search match | normal: `` N ``, `` ZN ``, `` zN ``, select: `` ZN ``, `` zN `` |
| `extend_search_next` | Add next search match to selection | select: `` n `` |
| `extend_search_prev` | Add previous search match to selection | select: `` N `` |
| `search_selection` | Use current selection as search pattern | normal: `` <A-*> ``, select: `` <A-*> `` |
| `search_selection_detect_word_boundaries` | Use current selection as the search pattern, automatically wrapping with `\b` on word boundaries | normal: `` * ``, select: `` * `` |
| `make_search_word_bounded` | Modify current search to make it word bounded | |
| `global_search` | Global search in workspace folder | normal: `` <space>/ ``, select: `` <space>/ `` |
| `extend_line` | Select current line, if already selected, extend to another line based on the anchor | |
| `extend_line_below` | Select current line, if already selected, extend to next line | normal: `` x ``, select: `` x `` |
| `extend_line_above` | Select current line, if already selected, extend to previous line | |
| `select_line_above` | Select current line, if already selected, extend or shrink line above based on the anchor | |
| `select_line_below` | Select current line, if already selected, extend or shrink line below based on the anchor | |
| `extend_to_line_bounds` | Extend selection to line bounds | normal: `` X ``, select: `` X `` |
| `shrink_to_line_bounds` | Shrink selection to line bounds | normal: `` <A-x> ``, select: `` <A-x> `` |
| `delete_selection` | Delete selection | normal: `` d ``, select: `` d `` |
| `delete_selection_noyank` | Delete selection without yanking | normal: `` <A-d> ``, select: `` <A-d> `` |
| `change_selection` | Change selection | normal: `` c ``, select: `` c `` |
| `change_selection_noyank` | Change selection without yanking | normal: `` <A-c> ``, select: `` <A-c> `` |
| `collapse_selection` | Collapse selection into single cursor | normal: `` ; ``, select: `` ; `` |
| `flip_selections` | Flip selection cursor and anchor | normal: `` <A-;> ``, select: `` <A-;> `` |
| `ensure_selections_forward` | Ensure all selections face forward | normal: `` <A-:> ``, select: `` <A-:> `` |
| `insert_mode` | Insert before selection | normal: `` i ``, select: `` i `` |
| `append_mode` | Append after selection | normal: `` a ``, select: `` a `` |
| `command_mode` | Enter command mode | normal: `` : ``, select: `` : `` |
| `file_picker` | Open file picker | normal: `` <space>f ``, select: `` <space>f `` |
| `file_picker_in_current_buffer_directory` | Open file picker at current buffer's directory | |
| `file_picker_in_current_directory` | Open file picker at current working directory | normal: `` <space>F ``, select: `` <space>F `` |
| `code_action` | Perform code action | normal: `` <space>a ``, select: `` <space>a `` |
| `buffer_picker` | Open buffer picker | normal: `` <space>b ``, select: `` <space>b `` |
| `jumplist_picker` | Open jumplist picker | normal: `` <space>j ``, select: `` <space>j `` |
| `symbol_picker` | Open symbol picker | normal: `` <space>s ``, select: `` <space>s `` |
| `changed_file_picker` | Open changed file picker | normal: `` <space>g ``, select: `` <space>g `` |
| `select_references_to_symbol_under_cursor` | Select symbol references | normal: `` <space>h ``, select: `` <space>h `` |
| `workspace_symbol_picker` | Open workspace symbol picker | normal: `` <space>S ``, select: `` <space>S `` |
| `diagnostics_picker` | Open diagnostic picker | normal: `` <space>d ``, select: `` <space>d `` |
| `workspace_diagnostics_picker` | Open workspace diagnostic picker | normal: `` <space>D ``, select: `` <space>D `` |
| `last_picker` | Open last picker | normal: `` <space>' ``, select: `` <space>' `` |
| `insert_at_line_start` | Insert at start of line | normal: `` I ``, select: `` I `` |
| `insert_at_line_end` | Insert at end of line | normal: `` A ``, select: `` A `` |
| `open_below` | Open new line below selection | normal: `` o ``, select: `` o `` |
| `open_above` | Open new line above selection | normal: `` O ``, select: `` O `` |
| `normal_mode` | Enter normal mode | normal: `` <esc> ``, select: `` v ``, insert: `` <esc> `` |
| `select_mode` | Enter selection extend mode | normal: `` v `` |
| `exit_select_mode` | Exit selection mode | select: `` <esc> `` |
| `goto_definition` | Goto definition | normal: `` gd ``, select: `` gd `` |
| `goto_declaration` | Goto declaration | normal: `` gD ``, select: `` gD `` |
| `add_newline_above` | Add newline above | normal: `` [<space> ``, select: `` [<space> `` |
| `add_newline_below` | Add newline below | normal: `` ]<space> ``, select: `` ]<space> `` |
| `goto_type_definition` | Goto type definition | normal: `` gy ``, select: `` gy `` |
| `goto_implementation` | Goto implementation | normal: `` gi ``, select: `` gi `` |
| `goto_file_start` | Goto line number <n> else file start | normal: `` gg ``, select: `` gg `` |
| `goto_file_end` | Goto file end | |
| `goto_file` | Goto files/URLs in selections | normal: `` gf ``, select: `` gf `` |
| `goto_file_hsplit` | Goto files in selections (hsplit) | normal: `` <C-w>f ``, `` <space>wf ``, select: `` <C-w>f ``, `` <space>wf `` |
| `goto_file_vsplit` | Goto files in selections (vsplit) | normal: `` <C-w>F ``, `` <space>wF ``, select: `` <C-w>F ``, `` <space>wF `` |
| `goto_reference` | Goto references | normal: `` gr ``, select: `` gr `` |
| `goto_window_top` | Goto window top | normal: `` gt ``, select: `` gt `` |
| `goto_window_center` | Goto window center | normal: `` gc ``, select: `` gc `` |
| `goto_window_bottom` | Goto window bottom | normal: `` gb ``, select: `` gb `` |
| `goto_last_accessed_file` | Goto last accessed file | normal: `` ga ``, select: `` ga `` |
| `goto_last_modified_file` | Goto last modified file | normal: `` gm ``, select: `` gm `` |
| `goto_last_modification` | Goto last modification | normal: `` g. ``, select: `` g. `` |
| `goto_line` | Goto line | normal: `` G ``, select: `` G `` |
| `goto_last_line` | Goto last line | normal: `` ge ``, select: `` ge `` |
| `goto_first_diag` | Goto first diagnostic | normal: `` [D ``, select: `` [D `` |
| `goto_last_diag` | Goto last diagnostic | normal: `` ]D ``, select: `` ]D `` |
| `goto_next_diag` | Goto next diagnostic | normal: `` ]d ``, select: `` ]d `` |
| `goto_prev_diag` | Goto previous diagnostic | normal: `` [d ``, select: `` [d `` |
| `goto_next_change` | Goto next change | normal: `` ]g ``, select: `` ]g `` |
| `goto_prev_change` | Goto previous change | normal: `` [g ``, select: `` [g `` |
| `goto_first_change` | Goto first change | normal: `` [G ``, select: `` [G `` |
| `goto_last_change` | Goto last change | normal: `` ]G ``, select: `` ]G `` |
| `goto_line_start` | Goto line start | normal: `` gh ``, `` <home> ``, select: `` gh ``, insert: `` <home> `` |
| `goto_line_end` | Goto line end | normal: `` gl ``, `` <end> ``, select: `` gl `` |
| `goto_next_buffer` | Goto next buffer | normal: `` gn ``, select: `` gn `` |
| `goto_previous_buffer` | Goto previous buffer | normal: `` gp ``, select: `` gp `` |
| `goto_line_end_newline` | Goto newline at line end | insert: `` <end> `` |
| `goto_first_nonwhitespace` | Goto first non-blank in line | normal: `` gs ``, select: `` gs `` |
| `trim_selections` | Trim whitespace from selections | normal: `` _ ``, select: `` _ `` |
| `extend_to_line_start` | Extend to line start | select: `` <home> `` |
| `extend_to_first_nonwhitespace` | Extend to first non-blank in line | |
| `extend_to_line_end` | Extend to line end | select: `` <end> `` |
| `extend_to_line_end_newline` | Extend to line end | |
| `signature_help` | Show signature help | |
| `smart_tab` | Insert tab if all cursors have all whitespace to their left; otherwise, run a separate command. | insert: `` <tab> `` |
| `insert_tab` | Insert tab char | insert: `` <S-tab> `` |
| `insert_newline` | Insert newline char | insert: `` <C-j> ``, `` <ret> `` |
| `delete_char_backward` | Delete previous char | insert: `` <C-h> ``, `` <backspace> ``, `` <S-backspace> `` |
| `delete_char_forward` | Delete next char | insert: `` <C-d> ``, `` <del> `` |
| `delete_word_backward` | Delete previous word | insert: `` <C-w> ``, `` <A-backspace> `` |
| `delete_word_forward` | Delete next word | insert: `` <A-d> ``, `` <A-del> `` |
| `kill_to_line_start` | Delete till start of line | insert: `` <C-u> `` |
| `kill_to_line_end` | Delete till end of line | insert: `` <C-k> `` |
| `undo` | Undo change | normal: `` u ``, select: `` u `` |
| `redo` | Redo change | normal: `` U ``, select: `` U `` |
| `earlier` | Move backward in history | normal: `` <A-u> ``, select: `` <A-u> `` |
| `later` | Move forward in history | normal: `` <A-U> ``, select: `` <A-U> `` |
| `commit_undo_checkpoint` | Commit changes to new checkpoint | insert: `` <C-s> `` |
| `yank` | Yank selection | normal: `` y ``, select: `` y `` |
| `yank_to_clipboard` | Yank selections to clipboard | normal: `` <space>y ``, select: `` <space>y `` |
| `yank_to_primary_clipboard` | Yank selections to primary clipboard | |
| `yank_joined` | Join and yank selections | |
| `yank_joined_to_clipboard` | Join and yank selections to clipboard | |
| `yank_main_selection_to_clipboard` | Yank main selection to clipboard | normal: `` <space>Y ``, select: `` <space>Y `` |
| `yank_joined_to_primary_clipboard` | Join and yank selections to primary clipboard | |
| `yank_main_selection_to_primary_clipboard` | Yank main selection to primary clipboard | |
| `replace_with_yanked` | Replace with yanked text | normal: `` R ``, select: `` R `` |
| `replace_selections_with_clipboard` | Replace selections by clipboard content | normal: `` <space>R ``, select: `` <space>R `` |
| `replace_selections_with_primary_clipboard` | Replace selections by primary clipboard | |
| `paste_after` | Paste after selection | normal: `` p ``, select: `` p `` |
| `paste_before` | Paste before selection | normal: `` P ``, select: `` P `` |
| `paste_clipboard_after` | Paste clipboard after selections | normal: `` <space>p ``, select: `` <space>p `` |
| `paste_clipboard_before` | Paste clipboard before selections | normal: `` <space>P ``, select: `` <space>P `` |
| `paste_primary_clipboard_after` | Paste primary clipboard after selections | |
| `paste_primary_clipboard_before` | Paste primary clipboard before selections | |
| `indent` | Indent selection | normal: `` <gt> ``, select: `` <gt> `` |
| `unindent` | Unindent selection | normal: `` <lt> ``, select: `` <lt> `` |
| `format_selections` | Format selection | normal: `` = ``, select: `` = `` |
| `join_selections` | Join lines inside selection | normal: `` J ``, select: `` J `` |
| `join_selections_space` | Join lines inside selection and select spaces | normal: `` <A-J> ``, select: `` <A-J> `` |
| `keep_selections` | Keep selections matching regex | normal: `` K ``, select: `` K `` |
| `remove_selections` | Remove selections matching regex | normal: `` <A-K> ``, select: `` <A-K> `` |
| `align_selections` | Align selections in column | normal: `` & ``, select: `` & `` |
| `keep_primary_selection` | Keep primary selection | normal: `` , ``, select: `` , `` |
| `remove_primary_selection` | Remove primary selection | normal: `` <A-,> ``, select: `` <A-,> `` |
| `completion` | Invoke completion popup | insert: `` <C-x> `` |
| `hover` | Show docs for item under cursor | normal: `` <space>k ``, select: `` <space>k `` |
| `toggle_comments` | Comment/uncomment selections | normal: `` <C-c> ``, `` <space>c ``, select: `` <C-c> ``, `` <space>c `` |
| `toggle_line_comments` | Line comment/uncomment selections | normal: `` <space><A-c> ``, select: `` <space><A-c> `` |
| `toggle_block_comments` | Block comment/uncomment selections | normal: `` <space>C ``, select: `` <space>C `` |
| `rotate_selections_forward` | Rotate selections forward | normal: `` ) ``, select: `` ) `` |
| `rotate_selections_backward` | Rotate selections backward | normal: `` ( ``, select: `` ( `` |
| `rotate_selection_contents_forward` | Rotate selection contents forward | normal: `` <A-)> ``, select: `` <A-)> `` |
| `rotate_selection_contents_backward` | Rotate selections contents backward | normal: `` <A-(> ``, select: `` <A-(> `` |
| `reverse_selection_contents` | Reverse selections contents | |
| `expand_selection` | Expand selection to parent syntax node | normal: `` <A-o> ``, `` <A-up> ``, select: `` <A-o> ``, `` <A-up> `` |
| `shrink_selection` | Shrink selection to previously expanded syntax node | normal: `` <A-i> ``, `` <A-down> ``, select: `` <A-i> ``, `` <A-down> `` |
| `select_next_sibling` | Select next sibling in the syntax tree | normal: `` <A-n> ``, `` <A-right> ``, select: `` <A-n> ``, `` <A-right> `` |
| `select_prev_sibling` | Select previous sibling the in syntax tree | normal: `` <A-p> ``, `` <A-left> ``, select: `` <A-p> ``, `` <A-left> `` |
| `select_all_siblings` | Select all siblings of the current node | normal: `` <A-a> ``, select: `` <A-a> `` |
| `select_all_children` | Select all children of the current node | normal: `` <A-I> ``, `` <S-A-down> ``, select: `` <A-I> ``, `` <S-A-down> `` |
| `jump_forward` | Jump forward on jumplist | normal: `` <C-i> ``, `` <tab> ``, select: `` <C-i> ``, `` <tab> `` |
| `jump_backward` | Jump backward on jumplist | normal: `` <C-o> ``, select: `` <C-o> `` |
| `save_selection` | Save current selection to jumplist | normal: `` <C-s> ``, select: `` <C-s> `` |
| `jump_view_right` | Jump to right split | normal: `` <C-w>l ``, `` <space>wl ``, `` <C-w><C-l> ``, `` <C-w><right> ``, `` <space>w<C-l> ``, `` <space>w<right> ``, select: `` <C-w>l ``, `` <space>wl ``, `` <C-w><C-l> ``, `` <C-w><right> ``, `` <space>w<C-l> ``, `` <space>w<right> `` |
| `jump_view_left` | Jump to left split | normal: `` <C-w>h ``, `` <space>wh ``, `` <C-w><C-h> ``, `` <C-w><left> ``, `` <space>w<C-h> ``, `` <space>w<left> ``, select: `` <C-w>h ``, `` <space>wh ``, `` <C-w><C-h> ``, `` <C-w><left> ``, `` <space>w<C-h> ``, `` <space>w<left> `` |
| `jump_view_up` | Jump to split above | normal: `` <C-w>k ``, `` <C-w><up> ``, `` <space>wk ``, `` <C-w><C-k> ``, `` <space>w<up> ``, `` <space>w<C-k> ``, select: `` <C-w>k ``, `` <C-w><up> ``, `` <space>wk ``, `` <C-w><C-k> ``, `` <space>w<up> ``, `` <space>w<C-k> `` |
| `jump_view_down` | Jump to split below | normal: `` <C-w>j ``, `` <space>wj ``, `` <C-w><C-j> ``, `` <C-w><down> ``, `` <space>w<C-j> ``, `` <space>w<down> ``, select: `` <C-w>j ``, `` <space>wj ``, `` <C-w><C-j> ``, `` <C-w><down> ``, `` <space>w<C-j> ``, `` <space>w<down> `` |
| `swap_view_right` | Swap with right split | normal: `` <C-w>L ``, `` <space>wL ``, select: `` <C-w>L ``, `` <space>wL `` |
| `swap_view_left` | Swap with left split | normal: `` <C-w>H ``, `` <space>wH ``, select: `` <C-w>H ``, `` <space>wH `` |
| `swap_view_up` | Swap with split above | normal: `` <C-w>K ``, `` <space>wK ``, select: `` <C-w>K ``, `` <space>wK `` |
| `swap_view_down` | Swap with split below | normal: `` <C-w>J ``, `` <space>wJ ``, select: `` <C-w>J ``, `` <space>wJ `` |
| `transpose_view` | Transpose splits | normal: `` <C-w>t ``, `` <space>wt ``, `` <C-w><C-t> ``, `` <space>w<C-t> ``, select: `` <C-w>t ``, `` <space>wt ``, `` <C-w><C-t> ``, `` <space>w<C-t> `` |
| `rotate_view` | Goto next window | normal: `` <C-w>w ``, `` <space>ww ``, `` <C-w><C-w> ``, `` <space>w<C-w> ``, select: `` <C-w>w ``, `` <space>ww ``, `` <C-w><C-w> ``, `` <space>w<C-w> `` |
| `rotate_view_reverse` | Goto previous window | |
| `hsplit` | Horizontal bottom split | normal: `` <C-w>s ``, `` <space>ws ``, `` <C-w><C-s> ``, `` <space>w<C-s> ``, select: `` <C-w>s ``, `` <space>ws ``, `` <C-w><C-s> ``, `` <space>w<C-s> `` |
| `hsplit_new` | Horizontal bottom split scratch buffer | normal: `` <C-w>ns ``, `` <space>wns ``, `` <C-w>n<C-s> ``, `` <space>wn<C-s> ``, select: `` <C-w>ns ``, `` <space>wns ``, `` <C-w>n<C-s> ``, `` <space>wn<C-s> `` |
| `vsplit` | Vertical right split | normal: `` <C-w>v ``, `` <space>wv ``, `` <C-w><C-v> ``, `` <space>w<C-v> ``, select: `` <C-w>v ``, `` <space>wv ``, `` <C-w><C-v> ``, `` <space>w<C-v> `` |
| `vsplit_new` | Vertical right split scratch buffer | normal: `` <C-w>nv ``, `` <space>wnv ``, `` <C-w>n<C-v> ``, `` <space>wn<C-v> ``, select: `` <C-w>nv ``, `` <space>wnv ``, `` <C-w>n<C-v> ``, `` <space>wn<C-v> `` |
| `wclose` | Close window | normal: `` <C-w>q ``, `` <space>wq ``, `` <C-w><C-q> ``, `` <space>w<C-q> ``, select: `` <C-w>q ``, `` <space>wq ``, `` <C-w><C-q> ``, `` <space>w<C-q> `` |
| `wonly` | Close windows except current | normal: `` <C-w>o ``, `` <space>wo ``, `` <C-w><C-o> ``, `` <space>w<C-o> ``, select: `` <C-w>o ``, `` <space>wo ``, `` <C-w><C-o> ``, `` <space>w<C-o> `` |
| `select_register` | Select register | normal: `` " ``, select: `` " `` |
| `insert_register` | Insert register | insert: `` <C-r> `` |
| `align_view_middle` | Align view middle | normal: `` Zm ``, `` zm ``, select: `` Zm ``, `` zm `` |
| `align_view_top` | Align view top | normal: `` Zt ``, `` zt ``, select: `` Zt ``, `` zt `` |
| `align_view_center` | Align view center | normal: `` Zc ``, `` Zz ``, `` zc ``, `` zz ``, select: `` Zc ``, `` Zz ``, `` zc ``, `` zz `` |
| `align_view_bottom` | Align view bottom | normal: `` Zb ``, `` zb ``, select: `` Zb ``, `` zb `` |
| `scroll_up` | Scroll view up | normal: `` Zk ``, `` zk ``, `` Z<up> ``, `` z<up> ``, select: `` Zk ``, `` zk ``, `` Z<up> ``, `` z<up> `` |
| `scroll_down` | Scroll view down | normal: `` Zj ``, `` zj ``, `` Z<down> ``, `` z<down> ``, select: `` Zj ``, `` zj ``, `` Z<down> ``, `` z<down> `` |
| `match_brackets` | Goto matching bracket | normal: `` mm ``, select: `` mm `` |
| `surround_add` | Surround add | normal: `` ms ``, select: `` ms `` |
| `surround_replace` | Surround replace | normal: `` mr ``, select: `` mr `` |
| `surround_delete` | Surround delete | normal: `` md ``, select: `` md `` |
| `select_textobject_around` | Select around object | normal: `` ma ``, select: `` ma `` |
| `select_textobject_inner` | Select inside object | normal: `` mi ``, select: `` mi `` |
| `goto_next_function` | Goto next function | normal: `` ]f ``, select: `` ]f `` |
| `goto_prev_function` | Goto previous function | normal: `` [f ``, select: `` [f `` |
| `goto_next_class` | Goto next type definition | normal: `` ]t ``, select: `` ]t `` |
| `goto_prev_class` | Goto previous type definition | normal: `` [t ``, select: `` [t `` |
| `goto_next_parameter` | Goto next parameter | normal: `` ]a ``, select: `` ]a `` |
| `goto_prev_parameter` | Goto previous parameter | normal: `` [a ``, select: `` [a `` |
| `goto_next_comment` | Goto next comment | normal: `` ]c ``, select: `` ]c `` |
| `goto_prev_comment` | Goto previous comment | normal: `` [c ``, select: `` [c `` |
| `goto_next_test` | Goto next test | normal: `` ]T ``, select: `` ]T `` |
| `goto_prev_test` | Goto previous test | normal: `` [T ``, select: `` [T `` |
| `goto_next_entry` | Goto next pairing | normal: `` ]e ``, select: `` ]e `` |
| `goto_prev_entry` | Goto previous pairing | normal: `` [e ``, select: `` [e `` |
| `goto_next_paragraph` | Goto next paragraph | normal: `` ]p ``, select: `` ]p `` |
| `goto_prev_paragraph` | Goto previous paragraph | normal: `` [p ``, select: `` [p `` |
| `dap_launch` | Launch debug target | normal: `` <space>Gl ``, select: `` <space>Gl `` |
| `dap_restart` | Restart debugging session | normal: `` <space>Gr ``, select: `` <space>Gr `` |
| `dap_toggle_breakpoint` | Toggle breakpoint | normal: `` <space>Gb ``, select: `` <space>Gb `` |
| `dap_continue` | Continue program execution | normal: `` <space>Gc ``, select: `` <space>Gc `` |
| `dap_pause` | Pause program execution | normal: `` <space>Gh ``, select: `` <space>Gh `` |
| `dap_step_in` | Step in | normal: `` <space>Gi ``, select: `` <space>Gi `` |
| `dap_step_out` | Step out | normal: `` <space>Go ``, select: `` <space>Go `` |
| `dap_next` | Step to next | normal: `` <space>Gn ``, select: `` <space>Gn `` |
| `dap_variables` | List variables | normal: `` <space>Gv ``, select: `` <space>Gv `` |
| `dap_terminate` | End debug session | normal: `` <space>Gt ``, select: `` <space>Gt `` |
| `dap_edit_condition` | Edit breakpoint condition on current line | normal: `` <space>G<C-c> ``, select: `` <space>G<C-c> `` |
| `dap_edit_log` | Edit breakpoint log message on current line | normal: `` <space>G<C-l> ``, select: `` <space>G<C-l> `` |
| `dap_switch_thread` | Switch current thread | normal: `` <space>Gst ``, select: `` <space>Gst `` |
| `dap_switch_stack_frame` | Switch stack frame | normal: `` <space>Gsf ``, select: `` <space>Gsf `` |
| `dap_enable_exceptions` | Enable exception breakpoints | normal: `` <space>Ge ``, select: `` <space>Ge `` |
| `dap_disable_exceptions` | Disable exception breakpoints | normal: `` <space>GE ``, select: `` <space>GE `` |
| `shell_pipe` | Pipe selections through shell command | normal: `` \| ``, select: `` \| `` |
| `shell_pipe_to` | Pipe selections into shell command ignoring output | normal: `` <A-\|> ``, select: `` <A-\|> `` |
| `shell_insert_output` | Insert shell command output before selections | normal: `` ! ``, select: `` ! `` |
| `shell_append_output` | Append shell command output after selections | normal: `` <A-!> ``, select: `` <A-!> `` |
| `shell_keep_pipe` | Filter selections with shell predicate | normal: `` $ ``, select: `` $ `` |
| `suspend` | Suspend and return to shell | normal: `` <C-z> ``, select: `` <C-z> `` |
| `rename_symbol` | Rename symbol | normal: `` <space>r ``, select: `` <space>r `` |
| `increment` | Increment item under cursor | normal: `` <C-a> ``, select: `` <C-a> `` |
| `decrement` | Decrement item under cursor | normal: `` <C-x> ``, select: `` <C-x> `` |
| `record_macro` | Record macro | normal: `` Q ``, select: `` Q `` |
| `replay_macro` | Replay macro | normal: `` q ``, select: `` q `` |
| `command_palette` | Open command palette | normal: `` <space>? ``, select: `` <space>? `` |
| `goto_word` | Jump to a two-character label | normal: `` gw `` |
| `extend_to_word` | Extend to a two-character label | select: `` gw `` |

View file

@ -16,7 +16,7 @@
| `:write-buffer-close`, `:wbc` | Write changes to disk and closes the buffer. Accepts an optional path (:write-buffer-close some/path.txt) |
| `:write-buffer-close!`, `:wbc!` | Force write changes to disk creating necessary subdirectories and closes the buffer. Accepts an optional path (:write-buffer-close! some/path.txt) |
| `:new`, `:n` | Create a new scratch buffer. |
| `:format`, `:fmt` | Format the file using the LSP formatter. |
| `:format`, `:fmt` | Format the file using an external formatter or language server. |
| `:indent-style` | Set the indentation style for editing. ('t' for tabs or 1-16 for number of spaces.) |
| `:line-ending` | Set the document's default line ending. Options: crlf, lf. |
| `:earlier`, `:ear` | Jump back to an earlier point in edit history. Accepts a number of steps or a time span. |

View file

@ -160,7 +160,8 @@ Search commands all operate on the `/` register by default. To use a different r
| `?` | Search for previous pattern | `rsearch` |
| `n` | Select next search match | `search_next` |
| `N` | Select previous search match | `search_prev` |
| `*` | Use current selection as the search pattern | `search_selection` |
| `*` | Use current selection as the search pattern, automatically wrapping with `\b` on word boundaries | `search_selection_detect_word_boundaries` |
| `Alt-*` | Use current selection as the search pattern | `search_selection` |
### Minor modes

View file

@ -13,7 +13,7 @@ There are three possible locations for a `languages.toml` file:
2. In your [configuration directory](./configuration.md). This overrides values
from the built-in language configuration. For example, to disable
auto-LSP-formatting in Rust:
auto-formatting for Rust:
```toml
# in <config_dir>/helix/languages.toml
@ -128,7 +128,7 @@ These are the available options for a language server.
| ---- | ----------- |
| `command` | The name or path of the language server binary to execute. Binaries must be in `$PATH` |
| `args` | A list of arguments to pass to the language server binary |
| `config` | LSP initialization options |
| `config` | Language server initialization options |
| `timeout` | The maximum time a request to the language server may take, in seconds. Defaults to `20` |
| `environment` | Any environment variables that will be used when starting the language server `{ "KEY1" = "Value1", "KEY2" = "Value2" }` |
| `required-root-patterns` | A list of `glob` patterns to look for in the working directory. The language server is started if at least one of them is found. |

View file

@ -4,7 +4,7 @@ Helix has a variety of pickers, which are interactive windows used to select var
### Filtering Picker Results
Most pickers perform fuzzy matching using [fzf syntax](https://github.com/junegunn/fzf?tab=readme-ov-file#search-syntax). Two exceptions are the global search picker, which uses regex, and the workspace symbol picker, which passes search terms to the LSP. Note that OR operations (`|`) are not currently supported.
Most pickers perform fuzzy matching using [fzf syntax](https://github.com/junegunn/fzf?tab=readme-ov-file#search-syntax). Two exceptions are the global search picker, which uses regex, and the workspace symbol picker, which passes search terms to the language server. Note that OR operations (`|`) are not currently supported.
If a picker shows multiple columns, you may apply the filter to a specific column by prefixing the column name with `%`. Column names can be shortened to any prefix, so `%p`, `%pa` or `%pat` all mean the same as `%path`. For example, a query of `helix %p .toml !lang` in the global search picker searches for the term "helix" within files with paths ending in ".toml" but not including "lang".

View file

@ -72,8 +72,22 @@ t = ":run-shell-command cargo test"
## Special keys and modifiers
Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes
`C-`, `S-` and `A-`. Special keys are encoded as follows:
Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes `C-`, `S-` and `A-`.
The [Super key](https://en.wikipedia.org/wiki/Super_key_(keyboard_button)) - the Windows/Linux
key or the Command key on Mac keyboards - is also supported when using a terminal emulator that
supports the [enhanced keyboard protocol](https://github.com/helix-editor/helix/wiki/Terminal-Support#enhanced-keyboard-protocol).
The super key is encoded with prefixes `Meta-`, `Cmd-` or `Win-`. These are all synonyms for the
super modifier - binding a key with a `Win-` modifier will mean it can be used with the
Windows/Linux key or the Command key.
```toml
[keys.normal]
C-s = ":write" # Ctrl and 's' to write
Cmd-s = ":write" # Cmd or Win or Meta and 's' to write
```
Special keys are encoded as follows:
| Key name | Representation |
| --- | --- |

View file

@ -309,8 +309,8 @@ These scopes are used for theming the editor interface:
| `ui.virtual.whitespace` | Visible whitespace characters |
| `ui.virtual.indent-guide` | Vertical indent width guides |
| `ui.virtual.inlay-hint` | Default style for inlay hints of all kinds |
| `ui.virtual.inlay-hint.parameter` | Style for inlay hints of kind `parameter` (LSPs are not required to set a kind) |
| `ui.virtual.inlay-hint.type` | Style for inlay hints of kind `type` (LSPs are not required to set a kind) |
| `ui.virtual.inlay-hint.parameter` | Style for inlay hints of kind `parameter` (language servers are not required to set a kind) |
| `ui.virtual.inlay-hint.type` | Style for inlay hints of kind `type` (language servers are not required to set a kind) |
| `ui.virtual.wrap` | Soft-wrap indicator (see the [`editor.soft-wrap` config][editor-section]) |
| `ui.virtual.jump-label` | Style for virtual jump labels |
| `ui.menu` | Code and command completion menus |

View file

@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html lang="{{ language }}" class="{{ default_theme }}" dir="{{ text_direction }}">
<html lang="{{ language }}" class="{{ default_theme }} sidebar-visible" dir="{{ text_direction }}">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
@ -52,15 +52,17 @@
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{{/if}}
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "{{ path_to_root }}";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
</script>
<!-- Start loading toc.js asap -->
<script src="{{ path_to_root }}toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
@ -82,19 +84,16 @@
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
const html = document.documentElement;
html.classList.remove('{{ default_theme }}')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
@ -104,39 +103,21 @@
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
{{#toc}}{{/toc}}
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="{{ path_to_root }}toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">

View file

@ -59,10 +59,16 @@ the default value (e.g. to `10240` from `256`) by running `ulimit -n 10240`.
## Minimum Stable Rust Version (MSRV) Policy
Helix follows the MSRV of Firefox.
The current MSRV and future changes to the MSRV are listed in the [Firefox documentation].
Helix keeps an intentionally low MSRV for the sake of easy building and packaging
downstream. We follow [Firefox's MSRV policy]. Once Firefox's MSRV increases we
may bump ours as well, but be sure to check that popular distributions like Ubuntu
package the new MSRV version. When increasing the MSRV, update these three places:
[Firefox documentation]: https://firefox-source-docs.mozilla.org/writing-rust-code/update-policy.html
* the `workspace.package.rust-version` key in `Cargo.toml` in the repository root
* the `env.MSRV` key at the top of `.github/workflows/build.yml`
* the `toolchain.channel` key in `rust-toolchain.toml`
[Firefox's MSRV policy]: https://firefox-source-docs.mozilla.org/writing-rust-code/update-policy.html
[good-first-issue]: https://github.com/helix-editor/helix/labels/E-easy
[log-file]: https://github.com/helix-editor/helix/wiki/FAQ#access-the-log-file
[architecture.md]: ./architecture.md

View file

@ -39,7 +39,7 @@ bitflags = "2.6"
ahash = "0.8.11"
hashbrown = { version = "0.14.5", features = ["raw"] }
dunce = "1.0"
url = "2.5.3"
url = "2.5.4"
log = "0.4"
serde = { version = "1.0", features = ["derive"] }

View file

@ -9,7 +9,7 @@ use crate::{
use helix_stdx::rope::RopeSliceExt;
use std::borrow::Cow;
pub const DEFAULT_COMMENT_TOKEN: &str = "//";
pub const DEFAULT_COMMENT_TOKEN: &str = "#";
/// Returns the longest matching comment token of the given line (if it exists).
pub fn get_comment_token<'a, S: AsRef<str>>(
@ -147,10 +147,7 @@ pub fn find_block_comments(
let mut only_whitespace = true;
let mut comment_changes = Vec::with_capacity(selection.len());
let default_tokens = tokens.first().cloned().unwrap_or_default();
// TODO: check if this can be removed on MSRV bump
#[allow(clippy::redundant_clone)]
let mut start_token = default_tokens.start.clone();
#[allow(clippy::redundant_clone)]
let mut end_token = default_tokens.end.clone();
let mut tokens = tokens.to_vec();
@ -376,12 +373,12 @@ mod test {
let transaction = toggle_line_comments(&doc, &selection, None);
transaction.apply(&mut doc);
assert_eq!(doc, " // 1\n\n // 2\n // 3");
assert_eq!(doc, " # 1\n\n # 2\n # 3");
}
#[test]
fn uncomment() {
let mut doc = Rope::from(" // 1\n\n // 2\n // 3");
let mut doc = Rope::from(" # 1\n\n # 2\n # 3");
let mut selection = Selection::single(0, doc.len_chars() - 1);
let transaction = toggle_line_comments(&doc, &selection, None);
@ -394,7 +391,7 @@ mod test {
#[test]
fn uncomment_0_margin_comments() {
let mut doc = Rope::from(" //1\n\n //2\n //3");
let mut doc = Rope::from(" #1\n\n #2\n #3");
let mut selection = Selection::single(0, doc.len_chars() - 1);
let transaction = toggle_line_comments(&doc, &selection, None);
@ -407,7 +404,7 @@ mod test {
#[test]
fn uncomment_0_margin_comments_with_no_space() {
let mut doc = Rope::from("//");
let mut doc = Rope::from("#");
let mut selection = Selection::single(0, doc.len_chars() - 1);
let transaction = toggle_line_comments(&doc, &selection, None);

View file

@ -346,7 +346,7 @@ pub struct RopeGraphemes<'a> {
cursor: GraphemeCursor,
}
impl<'a> fmt::Debug for RopeGraphemes<'a> {
impl fmt::Debug for RopeGraphemes<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RopeGraphemes")
.field("text", &self.text)
@ -358,7 +358,7 @@ impl<'a> fmt::Debug for RopeGraphemes<'a> {
}
}
impl<'a> RopeGraphemes<'a> {
impl RopeGraphemes<'_> {
#[must_use]
pub fn new(slice: RopeSlice) -> RopeGraphemes {
let mut chunks = slice.chunks();
@ -423,7 +423,7 @@ pub struct RevRopeGraphemes<'a> {
cursor: GraphemeCursor,
}
impl<'a> fmt::Debug for RevRopeGraphemes<'a> {
impl fmt::Debug for RevRopeGraphemes<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RevRopeGraphemes")
.field("text", &self.text)
@ -435,7 +435,7 @@ impl<'a> fmt::Debug for RevRopeGraphemes<'a> {
}
}
impl<'a> RevRopeGraphemes<'a> {
impl RevRopeGraphemes<'_> {
#[must_use]
pub fn new(slice: RopeSlice) -> RevRopeGraphemes {
let (mut chunks, mut cur_chunk_start, _, _) = slice.chunks_at_byte(slice.len_bytes());
@ -542,7 +542,7 @@ impl<'a> From<&'a str> for GraphemeStr<'a> {
}
}
impl<'a> From<String> for GraphemeStr<'a> {
impl From<String> for GraphemeStr<'_> {
fn from(g: String) -> Self {
let len = g.len();
let ptr = Box::into_raw(g.into_bytes().into_boxed_slice()) as *mut u8;

View file

@ -386,7 +386,7 @@ enum IndentCaptureType<'a> {
Align(RopeSlice<'a>),
}
impl<'a> IndentCaptureType<'a> {
impl IndentCaptureType<'_> {
fn default_scope(&self) -> IndentScope {
match self {
IndentCaptureType::Indent | IndentCaptureType::IndentAlways => IndentScope::Tail,

View file

@ -660,7 +660,7 @@ impl Selection {
pub fn fragments<'a>(
&'a self,
text: RopeSlice<'a>,
) -> impl DoubleEndedIterator<Item = Cow<'a, str>> + ExactSizeIterator<Item = Cow<str>> + 'a
) -> impl DoubleEndedIterator<Item = Cow<'a, str>> + ExactSizeIterator<Item = Cow<'a, str>>
{
self.ranges.iter().map(move |range| range.fragment(text))
}
@ -744,7 +744,7 @@ pub struct LineRangeIter<'a> {
text: RopeSlice<'a>,
}
impl<'a> Iterator for LineRangeIter<'a> {
impl Iterator for LineRangeIter<'_> {
type Item = (usize, usize);
fn next(&mut self) -> Option<Self::Item> {

View file

@ -621,7 +621,7 @@ pub enum CapturedNode<'a> {
Grouped(Vec<Node<'a>>),
}
impl<'a> CapturedNode<'a> {
impl CapturedNode<'_> {
pub fn start_byte(&self) -> usize {
match self {
Self::Single(n) => n.start_byte(),
@ -1854,7 +1854,7 @@ struct HighlightIterLayer<'a> {
depth: u32,
}
impl<'a> fmt::Debug for HighlightIterLayer<'a> {
impl fmt::Debug for HighlightIterLayer<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HighlightIterLayer").finish()
}
@ -2111,7 +2111,7 @@ impl HighlightConfiguration {
}
}
impl<'a> HighlightIterLayer<'a> {
impl HighlightIterLayer<'_> {
// First, sort scope boundaries by their byte offset in the document. At a
// given position, emit scope endings before scope beginnings. Finally, emit
// scope boundaries from deeper layers first.
@ -2249,7 +2249,7 @@ fn intersect_ranges(
result
}
impl<'a> HighlightIter<'a> {
impl HighlightIter<'_> {
fn emit_event(
&mut self,
offset: usize,
@ -2304,7 +2304,7 @@ impl<'a> HighlightIter<'a> {
}
}
impl<'a> Iterator for HighlightIter<'a> {
impl Iterator for HighlightIter<'_> {
type Item = Result<HighlightEvent, Error>;
fn next(&mut self) -> Option<Self::Item> {

View file

@ -217,7 +217,7 @@ impl<'a> TreeCursor<'a> {
/// Returns an iterator over the children of the node the TreeCursor is on
/// at the time this is called.
pub fn children(&'a mut self) -> ChildIter {
pub fn children(&'a mut self) -> ChildIter<'a> {
let parent = self.node();
ChildIter {
@ -229,7 +229,7 @@ impl<'a> TreeCursor<'a> {
/// Returns an iterator over the named children of the node the TreeCursor is on
/// at the time this is called.
pub fn named_children(&'a mut self) -> ChildIter {
pub fn named_children(&'a mut self) -> ChildIter<'a> {
let parent = self.node();
ChildIter {

View file

@ -211,7 +211,7 @@ impl<A, M> Layer<'_, A, M> {
}
impl<'a, A, M> From<(&'a [A], M)> for Layer<'a, A, M> {
fn from((annotations, metadata): (&'a [A], M)) -> Layer<A, M> {
fn from((annotations, metadata): (&'a [A], M)) -> Layer<'a, A, M> {
Layer {
annotations,
current_index: Cell::new(0),

View file

@ -769,7 +769,7 @@ impl<'a> ChangeIterator<'a> {
}
}
impl<'a> Iterator for ChangeIterator<'a> {
impl Iterator for ChangeIterator<'_> {
type Item = Change;
fn next(&mut self) -> Option<Self::Item> {

View file

@ -1,176 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "lsp-types"
version = "0.95.1"
dependencies = [
"bitflags",
"serde",
"serde_json",
"serde_repr",
"url",
]
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "proc-macro2"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "serde"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "unicode-bidi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]

View file

@ -25,7 +25,7 @@ bitflags = "2.6.0"
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
serde_repr = "0.1"
url = {version = "2.5.3", features = ["serde"]}
url = {version = "2.5.4", features = ["serde"]}
[features]
default = []

View file

@ -497,7 +497,6 @@ pub struct CompletionItem {
/// insertText is ignored.
///
/// Most editors support two different operation when accepting a completion item. One is to insert a
/// completion text and the other is to replace an existing text with a completion text. Since this can
/// usually not predetermined by a server it can report both ranges. Clients need to signal support for
/// `InsertReplaceEdits` via the `textDocument.completion.insertReplaceSupport` client capability

View file

@ -137,7 +137,7 @@ impl Serialize for Version {
struct VersionVisitor;
impl<'v> Visitor<'v> for VersionVisitor {
impl Visitor<'_> for VersionVisitor {
type Value = Version;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {

View file

@ -43,7 +43,7 @@ pub trait Parser<'a> {
#[doc(hidden)]
impl<'a, F, T> Parser<'a> for F
where
F: Fn(&'a str) -> ParseResult<T>,
F: Fn(&'a str) -> ParseResult<'a, T>,
{
type Output = T;

View file

@ -19,7 +19,7 @@ which = "7.0"
regex-cursor = "0.1.4"
bitflags = "2.6"
once_cell = "1.19"
regex-automata = "0.4.8"
regex-automata = "0.4.9"
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Threading"] }

View file

@ -7,6 +7,7 @@ use std::{
use once_cell::sync::Lazy;
// We keep the CWD as a static so that we can access it in places where we don't have access to the Editor
static CWD: RwLock<Option<PathBuf>> = RwLock::new(None);
// Get the current working directory.
@ -36,12 +37,12 @@ pub fn current_working_dir() -> PathBuf {
cwd
}
pub fn set_current_working_dir(path: impl AsRef<Path>) -> std::io::Result<()> {
pub fn set_current_working_dir(path: impl AsRef<Path>) -> std::io::Result<Option<PathBuf>> {
let path = crate::path::canonicalize(path);
std::env::set_current_dir(&path)?;
let mut cwd = CWD.write().unwrap();
*cwd = Some(path);
Ok(())
Ok(cwd.replace(path))
}
pub fn env_var_is_set(env_var_name: &str) -> bool {

View file

@ -60,7 +60,7 @@ thiserror.workspace = true
# opening URLs
open = "5.3.1"
url = "2.5.3"
url = "2.5.4"
# config
toml = "0.8"
@ -74,7 +74,7 @@ grep-searcher = "0.1.14"
[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
libc = "0.2.164"
libc = "0.2.167"
[target.'cfg(target_os = "macos")'.dependencies]
crossterm = { version = "0.28", features = ["event-stream", "use-dev-tty", "libc"] }

View file

@ -174,7 +174,7 @@ impl Application {
nr_of_files += 1;
if file.is_dir() {
return Err(anyhow::anyhow!(
"expected a path to file, found a directory. (to open a directory pass it as first argument)"
"expected a path to file, but found a directory: {file:?}. (to open a directory pass it as first argument)"
));
} else {
// If the user passes in either `--vsplit` or
@ -188,6 +188,7 @@ impl Application {
Some(Layout::Horizontal) => Action::HorizontalSplit,
None => Action::Load,
};
let old_id = editor.document_id_by_path(&file);
let doc_id = match editor.open(&file, action) {
// Ignore irregular files during application init.
Err(DocumentOpenError::IrregularFile) => {
@ -195,6 +196,11 @@ impl Application {
continue;
}
Err(err) => return Err(anyhow::anyhow!(err)),
// We can't open more than 1 buffer for 1 file, in this case we already have opened this file previously
Ok(doc_id) if old_id == Some(doc_id) => {
nr_of_files -= 1;
doc_id
}
Ok(doc_id) => doc_id,
};
// with Action::Load all documents have the same view

View file

@ -98,7 +98,7 @@ pub struct Context<'a> {
pub jobs: &'a mut Jobs,
}
impl<'a> Context<'a> {
impl Context<'_> {
/// Push a new component onto the compositor.
pub fn push_layer(&mut self, component: Box<dyn Component>) {
self.callback
@ -353,6 +353,7 @@ 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",
search_selection_detect_word_boundaries, "Use current selection as the search pattern, automatically wrapping with `\\b` on word boundaries",
make_search_word_bounded, "Modify current search to make it word bounded",
global_search, "Global search in workspace folder",
extend_line, "Select current line, if already selected, extend to another line based on the anchor",
@ -1289,7 +1290,6 @@ fn goto_file_impl(cx: &mut Context, action: Action) {
// rarely so in practice) so that gf on quoted/braced path works (not sure about this
// but apparently that is how gf has worked historically in helix)
let path = find_paths(search_range, true)
.inspect(|mat| println!("{mat:?} {:?}", pos - search_start))
.take_while(|range| search_start + range.start <= pos + 1)
.find(|range| pos <= search_start + range.end)
.map(|range| Cow::from(search_range.byte_slice(range)));
@ -2247,14 +2247,53 @@ fn extend_search_prev(cx: &mut Context) {
}
fn search_selection(cx: &mut Context) {
search_selection_impl(cx, false)
}
fn search_selection_detect_word_boundaries(cx: &mut Context) {
search_selection_impl(cx, true)
}
fn search_selection_impl(cx: &mut Context, detect_word_boundaries: bool) {
fn is_at_word_start(text: RopeSlice, index: usize) -> bool {
let ch = text.char(index);
if index == 0 {
return char_is_word(ch);
}
let prev_ch = text.char(index - 1);
!char_is_word(prev_ch) && char_is_word(ch)
}
fn is_at_word_end(text: RopeSlice, index: usize) -> bool {
if index == 0 || index == text.len_chars() {
return false;
}
let ch = text.char(index);
let prev_ch = text.char(index - 1);
char_is_word(prev_ch) && !char_is_word(ch)
}
let register = cx.register.unwrap_or('/');
let (view, doc) = current!(cx.editor);
let contents = doc.text().slice(..);
let text = doc.text().slice(..);
let regex = doc
.selection(view.id)
.iter()
.map(|selection| regex::escape(&selection.fragment(contents)))
.map(|selection| {
let add_boundary_prefix =
detect_word_boundaries && is_at_word_start(text, selection.from());
let add_boundary_suffix =
detect_word_boundaries && is_at_word_end(text, selection.to());
let prefix = if add_boundary_prefix { "\\b" } else { "" };
let suffix = if add_boundary_suffix { "\\b" } else { "" };
let word = regex::escape(&selection.fragment(text));
format!("{}{}{}", prefix, word, suffix)
})
.collect::<HashSet<_>>() // Collect into hashset to deduplicate identical regexes
.into_iter()
.collect::<Vec<_>>()
@ -3423,40 +3462,42 @@ fn open(cx: &mut Context, open: Open) {
let selection = doc.selection(view.id);
let mut ranges = SmallVec::with_capacity(selection.len());
let mut offs = 0;
let mut transaction = Transaction::change_by_selection(contents, selection, |range| {
let cursor_line = text.char_to_line(match open {
// the line number, where the cursor is currently
let curr_line_num = text.char_to_line(match open {
Open::Below => graphemes::prev_grapheme_boundary(text, range.to()),
Open::Above => range.from(),
});
let new_line = match open {
// adjust position to the end of the line (next line - 1)
Open::Below => cursor_line + 1,
// adjust position to the end of the previous line (current line - 1)
Open::Above => cursor_line,
// the next line number, where the cursor will be, after finishing the transaction
let next_new_line_num = match open {
Open::Below => curr_line_num + 1,
Open::Above => curr_line_num,
};
let line_num = new_line.saturating_sub(1);
let above_next_new_line_num = next_new_line_num.saturating_sub(1);
let continue_comment_token = if doc.config.load().continue_comments {
doc.language_config()
.and_then(|config| config.comment_tokens.as_ref())
.and_then(|tokens| comment::get_comment_token(text, tokens, curr_line_num))
} else {
None
};
// Index to insert newlines after, as well as the char width
// to use to compensate for those inserted newlines.
let (line_end_index, line_end_offset_width) = if new_line == 0 {
let (above_next_line_end_index, above_next_line_end_width) = if next_new_line_num == 0 {
(0, 0)
} else {
(
line_end_char_index(&text, line_num),
line_end_char_index(&text, above_next_new_line_num),
doc.line_ending.len_chars(),
)
};
let continue_comment_token = doc
.language_config()
.and_then(|config| config.comment_tokens.as_ref())
.and_then(|tokens| comment::get_comment_token(text, tokens, cursor_line));
let line = text.line(cursor_line);
let line = text.line(curr_line_num);
let indent = match line.first_non_whitespace_char() {
Some(pos) if continue_comment_token.is_some() => line.slice(..pos).to_string(),
_ => indent::indent_for_newline(
@ -3466,26 +3507,36 @@ fn open(cx: &mut Context, open: Open) {
&doc.indent_style,
doc.tab_width(),
text,
line_num,
line_end_index,
cursor_line,
above_next_new_line_num,
above_next_line_end_index,
curr_line_num,
),
};
let indent_len = indent.len();
let mut text = String::with_capacity(1 + indent_len);
text.push_str(doc.line_ending.as_str());
text.push_str(&indent);
if let Some(token) = continue_comment_token {
text.push_str(token);
text.push(' ');
if open == Open::Above && next_new_line_num == 0 {
text.push_str(&indent);
if let Some(token) = continue_comment_token {
text.push_str(token);
text.push(' ');
}
text.push_str(doc.line_ending.as_str());
} else {
text.push_str(doc.line_ending.as_str());
text.push_str(&indent);
if let Some(token) = continue_comment_token {
text.push_str(token);
text.push(' ');
}
}
let text = text.repeat(count);
// calculate new selection ranges
let pos = offs + line_end_index + line_end_offset_width;
let pos = above_next_line_end_index + above_next_line_end_width;
let comment_len = continue_comment_token
.map(|token| token.len() + 1) // `+ 1` for the extra space added
.unwrap_or_default();
@ -3498,9 +3549,11 @@ fn open(cx: &mut Context, open: Open) {
));
}
offs += text.chars().count();
(line_end_index, line_end_index, Some(text.into()))
(
above_next_line_end_index,
above_next_line_end_index,
Some(text.into()),
)
});
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
@ -3926,27 +3979,22 @@ pub mod insert {
let curr = contents.get_char(pos).unwrap_or(' ');
let current_line = text.char_to_line(pos);
let line_is_only_whitespace = text
.line(current_line)
.chars()
.all(|char| char.is_ascii_whitespace());
let line_start = text.line_to_char(current_line);
let mut new_text = String::new();
let continue_comment_token = doc
.language_config()
.and_then(|config| config.comment_tokens.as_ref())
.and_then(|tokens| comment::get_comment_token(text, tokens, current_line));
// If the current line is all whitespace, insert a line ending at the beginning of
// the current line. This makes the current line empty and the new line contain the
// indentation of the old line.
let (from, to, local_offs) = if line_is_only_whitespace {
let line_start = text.line_to_char(current_line);
new_text.push_str(doc.line_ending.as_str());
(line_start, line_start, new_text.chars().count())
let continue_comment_token = if doc.config.load().continue_comments {
doc.language_config()
.and_then(|config| config.comment_tokens.as_ref())
.and_then(|tokens| comment::get_comment_token(text, tokens, current_line))
} else {
None
};
let (from, to, local_offs) = if let Some(idx) =
text.slice(line_start..pos).last_non_whitespace_char()
{
let first_trailing_whitespace_char = (line_start + idx + 1).min(pos);
let line = text.line(current_line);
let indent = match line.first_non_whitespace_char() {
@ -3999,20 +4047,34 @@ pub mod insert {
new_text.chars().count()
};
(pos, pos, local_offs)
(
first_trailing_whitespace_char,
pos,
// Note that `first_trailing_whitespace_char` is at least `pos` so the
// unsigned subtraction (`pos - first_trailing_whitespace_char`) cannot
// underflow.
local_offs as isize - (pos - first_trailing_whitespace_char) as isize,
)
} else {
// If the current line is all whitespace, insert a line ending at the beginning of
// the current line. This makes the current line empty and the new line contain the
// indentation of the old line.
new_text.push_str(doc.line_ending.as_str());
(line_start, line_start, new_text.chars().count() as isize)
};
let new_range = if range.cursor(text) > range.anchor {
// when appending, extend the range by local_offs
Range::new(
range.anchor + global_offs,
range.head + local_offs + global_offs,
(range.head as isize + local_offs) as usize + global_offs,
)
} else {
// when inserting, slide the range by local_offs
Range::new(
range.anchor + local_offs + global_offs,
range.head + local_offs + global_offs,
(range.anchor as isize + local_offs) as usize + global_offs,
(range.head as isize + local_offs) as usize + global_offs,
)
};

View file

@ -9,6 +9,7 @@ use super::*;
use helix_core::fuzzy::fuzzy_match;
use helix_core::indent::MAX_INDENT;
use helix_core::{line_ending, shellwords::Shellwords};
use helix_stdx::path::home_dir;
use helix_view::document::{read_to_string, DEFAULT_LANGUAGE_NAME};
use helix_view::editor::{CloseError, ConfigEvent};
use serde_json::Value;
@ -456,13 +457,15 @@ fn format(
}
let (view, doc) = current!(cx.editor);
if let Some(format) = doc.format() {
let callback = make_format_callback(doc.id(), doc.version(), view.id, format, None);
cx.jobs.callback(callback);
}
let format = doc.format().context(
"A formatter isn't available, and no language server provides formatting capabilities",
)?;
let callback = make_format_callback(doc.id(), doc.version(), view.id, format, None);
cx.jobs.callback(callback);
Ok(())
}
fn set_indent_style(
cx: &mut compositor::Context,
args: &[Cow<str>],
@ -1087,18 +1090,23 @@ fn change_current_directory(
return Ok(());
}
let dir = args
.first()
.context("target directory not provided")?
.as_ref();
let dir = helix_stdx::path::expand_tilde(Path::new(dir));
let dir = match args.first().map(AsRef::as_ref) {
Some("-") => cx
.editor
.last_cwd
.clone()
.ok_or(anyhow!("No previous working directory"))?,
Some(input_path) => helix_stdx::path::expand_tilde(Path::new(input_path)).to_path_buf(),
None => home_dir()?,
};
helix_stdx::env::set_current_working_dir(dir)?;
cx.editor.last_cwd = helix_stdx::env::set_current_working_dir(dir)?;
cx.editor.set_status(format!(
"Current working directory is now {}",
helix_stdx::env::current_working_dir().display()
));
Ok(())
}
@ -2636,7 +2644,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "format",
aliases: &["fmt"],
doc: "Format the file using the LSP formatter.",
doc: "Format the file using an external formatter or language server.",
fun: format,
signature: CommandSignature::none(),
},

View file

@ -27,7 +27,7 @@ pub struct Context<'a> {
pub jobs: &'a mut Jobs,
}
impl<'a> Context<'a> {
impl Context<'_> {
/// Waits on all pending jobs, and then tries to flush all pending write
/// operations for all documents.
pub fn block_try_flush_writes(&mut self) -> anyhow::Result<()> {

View file

@ -154,7 +154,7 @@ pub fn languages_all() -> std::io::Result<()> {
}
};
let mut headings = vec!["Language", "LSP", "DAP", "Formatter"];
let mut headings = vec!["Language", "Language servers", "Debug adapter", "Formatter"];
for feat in TsFeature::all() {
headings.push(feat.short_title())

View file

@ -140,7 +140,8 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"?" => rsearch,
"n" => search_next,
"N" => search_prev,
"*" => search_selection,
"*" => search_selection_detect_word_boundaries,
"A-*" => search_selection,
"u" => undo,
"U" => redo,

View file

@ -154,8 +154,7 @@ FLAGS:
});
// TODO: use the thread local executor to spawn the application task separately from the work pool
let mut app =
Application::new(args, config, lang_loader).context("unable to create new application")?;
let mut app = Application::new(args, config, lang_loader).context("unable to start Helix")?;
let exit_code = app.run(&mut EventStream::new()).await?;

View file

@ -522,8 +522,6 @@ impl<'a> TextRenderer<'a> {
self.surface.set_style(area, style);
}
/// Sets the style of an area **within the text viewport* this accounts
/// both for the renderers vertical offset and its viewport
#[allow(clippy::too_many_arguments)]
pub fn set_string_truncated(
&mut self,

View file

@ -346,10 +346,6 @@ impl<T: Item + 'static> Component for Menu<T> {
let win_height = area.height as usize;
const fn div_ceil(a: usize, b: usize) -> usize {
(a + b - 1) / b
}
let rows = options
.iter()
.map(|option| option.format(&self.editor_data));
@ -390,7 +386,7 @@ impl<T: Item + 'static> Component for Menu<T> {
let scroll_style = theme.get("ui.menu.scroll");
if !fits {
let scroll_height = div_ceil(win_height.pow(2), len).min(win_height);
let scroll_height = win_height.pow(2).div_ceil(len).min(win_height);
let scroll_line = (win_height - scroll_height) * scroll
/ std::cmp::max(1, len.saturating_sub(win_height));

View file

@ -72,7 +72,7 @@ impl<'a> From<&'a Path> for PathOrId<'a> {
}
}
impl<'a> From<DocumentId> for PathOrId<'a> {
impl From<DocumentId> for PathOrId<'_> {
fn from(v: DocumentId) -> Self {
Self::Id(v)
}

View file

@ -344,12 +344,8 @@ impl<T: Component> Component for Popup<T> {
let fits = len <= win_height;
let scroll_style = cx.editor.theme.get("ui.menu.scroll");
const fn div_ceil(a: usize, b: usize) -> usize {
(a + b - 1) / b
}
if !fits {
let scroll_height = div_ceil(win_height.pow(2), len).min(win_height);
let scroll_height = win_height.pow(2).div_ceil(len).min(win_height);
let scroll_line = (win_height - scroll_height) * scroll
/ std::cmp::max(1, len.saturating_sub(win_height));

View file

@ -233,15 +233,7 @@ impl Prompt {
position
}
Movement::StartOfLine => 0,
Movement::EndOfLine => {
let mut cursor =
GraphemeCursor::new(self.line.len().saturating_sub(1), self.line.len(), false);
if let Ok(Some(pos)) = cursor.next_boundary(&self.line, 0) {
pos
} else {
self.cursor
}
}
Movement::EndOfLine => self.line.len(),
Movement::None => self.cursor,
}
}
@ -415,7 +407,8 @@ impl Prompt {
let cols = std::cmp::max(1, area.width / max_len);
let col_width = (area.width.saturating_sub(cols)) / cols;
let height = ((self.completion.len() as u16 + cols - 1) / cols)
let height = (self.completion.len() as u16)
.div_ceil(cols)
.min(10) // at most 10 rows (or less)
.min(area.height.saturating_sub(1));

View file

@ -98,20 +98,29 @@ impl Renderer<'_, '_> {
fn draw_eol_diagnostic(&mut self, diag: &Diagnostic, row: u16, col: usize) -> u16 {
let style = self.styles.severity_style(diag.severity());
let width = self.renderer.viewport.width;
if !self.renderer.column_in_bounds(col + 1, 1) {
return 0;
let start_col = (col - self.renderer.offset.col) as u16;
let mut end_col = start_col;
let mut draw_col = (col + 1) as u16;
for line in diag.message.lines() {
if !self.renderer.column_in_bounds(draw_col as usize, 1) {
break;
}
(end_col, _) = self.renderer.set_string_truncated(
self.renderer.viewport.x + draw_col,
row,
line,
width.saturating_sub(draw_col) as usize,
|_| style,
true,
false,
);
draw_col = end_col - self.renderer.viewport.x + 2; // double space between lines
}
let col = (col - self.renderer.offset.col) as u16;
let (new_col, _) = self.renderer.set_string_truncated(
self.renderer.viewport.x + col + 1,
row,
&diag.message,
width.saturating_sub(col + 1) as usize,
|_| style,
true,
false,
);
new_col - col
end_col - start_col
}
fn draw_diagnostic(&mut self, diag: &Diagnostic, col: u16, next_severity: Option<Severity>) {

View file

@ -2,6 +2,7 @@ use helix_term::application::Application;
use super::*;
mod insert;
mod movement;
mod write;

View file

@ -0,0 +1,246 @@
use super::*;
#[tokio::test(flavor = "multi_thread")]
async fn insert_newline_trim_trailing_whitespace() -> anyhow::Result<()> {
// Trailing whitespace is trimmed.
test((
indoc! {"\
hello·······#[|
]#world
"}
.replace('·', " "),
"i<ret>",
indoc! {"\
hello
#[|
]#world
"}
.replace('·', " "),
))
.await?;
// Whitespace that would become trailing is trimmed too.
test((
indoc! {"\
hello········#[|w]#orld
"}
.replace('·', " "),
"i<ret>",
indoc! {"\
hello
#[|w]#orld
"}
.replace('·', " "),
))
.await?;
// Only whitespace before the cursor is trimmed.
test((
indoc! {"\
hello········#[]#····world
"}
.replace('·', " "),
"i<ret>",
indoc! {"\
hello
#[]#····world
"}
.replace('·', " "),
))
.await?;
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_newline_continue_line_comment() -> anyhow::Result<()> {
// `insert_newline` continues a single line comment
test((
indoc! {"\
// Hello world!#[|
]#
"},
":lang rust<ret>i<ret>",
indoc! {"\
// Hello world!
// #[|
]#
"},
))
.await?;
// The comment is not continued if the cursor is before the comment token. (Note that we
// are entering insert-mode with `I`.)
test((
indoc! {"\
// Hello world!#[|
]#
"},
":lang rust<ret>I<ret>",
indoc! {"\
\n#[/|]#/ Hello world!
"},
))
.await?;
// `insert_newline` again clears the whitespace on the first continued comment and continues
// the comment again.
test((
indoc! {"\
// Hello world!
// #[|
]#
"},
":lang rust<ret>i<ret>",
indoc! {"\
// Hello world!
//
// #[|
]#
"},
))
.await?;
// Line comment continuation and trailing whitespace is also trimmed when using
// `insert_newline` in the middle of a comment.
test((
indoc! {"\
//·hello····#[|·]#····world
"}
.replace('·', " "),
":lang rust<ret>i<ret>",
indoc! {"\
//·hello
//·#[|·]#····world
"}
.replace('·', " "),
))
.await?;
Ok(())
}
/// NOTE: Language is set to markdown to check if the indentation is correct for the new line
#[tokio::test(flavor = "multi_thread")]
async fn test_open_above() -> anyhow::Result<()> {
// `O` is pressed in the first line
test((
indoc! {"Helix #[is|]# cool"},
":lang markdown<ret>O",
indoc! {"\
#[\n|]#
Helix is cool
"},
))
.await?;
// `O` is pressed in the first line, but the current line has some indentation
test((
indoc! {"\
··This line has 2 spaces in front of it#[\n|]#
"}
.replace('·', " "),
":lang markdown<ret>Oa",
indoc! {"\
··a#[\n|]#
··This line has 2 spaces in front of it
"}
.replace('·', " "),
))
.await?;
// `O` is pressed but *not* in the first line
test((
indoc! {"\
I use
b#[t|]#w.
"},
":lang markdown<ret>Oarch",
indoc! {"\
I use
arch#[\n|]#
btw.
"},
))
.await?;
// `O` is pressed but *not* in the first line and the line has some indentation
test((
indoc! {"\
I use
····b#[t|]#w.
"}
.replace("·", " "),
":lang markdown<ret>Ohelix",
indoc! {"\
I use
····helix#[\n|]#
····btw.
"}
.replace("·", " "),
))
.await?;
Ok(())
}
/// NOTE: To make the `open_above` comment-aware, we're setting the language for each test to rust.
#[tokio::test(flavor = "multi_thread")]
async fn test_open_above_with_comments() -> anyhow::Result<()> {
// `O` is pressed in the first line inside a line comment
test((
indoc! {"// a commen#[t|]#"},
":lang rust<ret>O",
indoc! {"\
// #[\n|]#
// a comment
"},
))
.await?;
// `O` is pressed in the first line inside a line comment, but with indentation
test((
indoc! {"····// a comm#[e|]#nt"}.replace("·", " "),
":lang rust<ret>O",
indoc! {"\
····// #[\n|]#
····// a comment
"}
.replace("·", " "),
))
.await?;
// `O` is pressed but not in the first line but inside a line comment
test((
indoc! {"\
fn main() { }
// yeetus deletus#[\n|]#
"},
":lang rust<ret>O",
indoc! {"\
fn main() { }
// #[\n|]#
// yeetus deletus
"},
))
.await?;
// `O` is pressed but not in the first line but inside a line comment and with indentation
test((
indoc! {"\
fn main() { }
····// yeetus deletus#[\n|]#
"}
.replace("·", " "),
":lang rust<ret>O",
indoc! {"\
fn main() { }
····// #[\n|]#
····// yeetus deletus
"}
.replace("·", " "),
))
.await?;
Ok(())
}

View file

@ -795,7 +795,7 @@ async fn auto_indent() -> anyhow::Result<()> {
"##},
"i<ret>",
indoc! {"\
foo:
foo:
#[|b]#ar
"},
),

View file

@ -212,7 +212,7 @@ impl<'a> From<Cow<'a, str>> for Span<'a> {
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Spans<'a>(pub Vec<Span<'a>>);
impl<'a> Spans<'a> {
impl Spans<'_> {
/// Returns the width of the underlying string.
///
/// ## Examples

View file

@ -123,7 +123,7 @@ impl<'a> Block<'a> {
}
}
impl<'a> Widget for Block<'a> {
impl Widget for Block<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
if area.area() == 0 {
return;

View file

@ -129,7 +129,7 @@ impl<'a> Paragraph<'a> {
}
}
impl<'a> Widget for Paragraph<'a> {
impl Widget for Paragraph<'_> {
fn render(mut self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
let text_area = match self.block.take() {

View file

@ -39,7 +39,7 @@ impl<'a, 'b> WordWrapper<'a, 'b> {
}
}
impl<'a, 'b> LineComposer<'a> for WordWrapper<'a, 'b> {
impl<'a> LineComposer<'a> for WordWrapper<'a, '_> {
fn next_line(&mut self) -> Option<(&[StyledGrapheme<'a>], u16)> {
if self.max_line_width == 0 {
return None;
@ -152,7 +152,7 @@ impl<'a, 'b> LineTruncator<'a, 'b> {
}
}
impl<'a, 'b> LineComposer<'a> for LineTruncator<'a, 'b> {
impl<'a> LineComposer<'a> for LineTruncator<'a, '_> {
fn next_line(&mut self) -> Option<(&[StyledGrapheme<'a>], u16)> {
if self.max_line_width == 0 {
return None;

View file

@ -34,7 +34,7 @@ pub struct Cell<'a> {
style: Style,
}
impl<'a> Cell<'a> {
impl Cell<'_> {
/// Set the `Style` of this cell.
pub fn style(mut self, style: Style) -> Self {
self.style = style;
@ -351,7 +351,7 @@ impl TableState {
}
// impl<'a> StatefulWidget for Table<'a> {
impl<'a> Table<'a> {
impl Table<'_> {
// type State = TableState;
pub fn render_table(
@ -486,7 +486,7 @@ fn render_cell(buf: &mut Buffer, cell: &Cell, area: Rect, truncate: bool) {
}
}
impl<'a> Widget for Table<'a> {
impl Widget for Table<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default();
Table::render_table(self, area, buf, &mut state, false);

View file

@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
parking_lot = "0.12"
arc-swap = { version = "1.7.1" }
gix = { version = "0.67.0", features = ["attributes", "status"], default-features = false, optional = true }
gix = { version = "0.68.0", features = ["attributes", "status"], default-features = false, optional = true }
imara-diff = "0.1.7"
anyhow = "1"

View file

@ -32,7 +32,7 @@ tempfile = "3.14"
# Conversion traits
once_cell = "1.20"
url = "2.5.3"
url = "2.5.4"
arc-swap = { version = "1.7.1" }

View file

@ -305,6 +305,9 @@ pub struct Config {
/// Whether to instruct the LSP to replace the entire word when applying a completion
/// or to only insert new text
pub completion_replace: bool,
/// `true` if helix should automatically add a line comment token if you're currently in a comment
/// and press `enter`.
pub continue_comments: bool,
/// Whether to display infoboxes. Defaults to true.
pub auto_info: bool,
pub file_picker: FilePickerConfig,
@ -986,6 +989,7 @@ impl Default for Config {
},
text_width: 80,
completion_replace: false,
continue_comments: true,
workspace_lsp_roots: Vec::new(),
default_line_ending: LineEndingConfig::default(),
insert_final_newline: true,
@ -1074,6 +1078,7 @@ pub struct Editor {
redraw_timer: Pin<Box<Sleep>>,
last_motion: Option<Motion>,
pub last_completion: Option<CompleteAction>,
pub last_cwd: Option<PathBuf>,
pub exit_code: i32,
@ -1207,6 +1212,7 @@ impl Editor {
redraw_timer: Box::pin(sleep(Duration::MAX)),
last_motion: None,
last_completion: None,
last_cwd: None,
config,
auto_pairs,
exit_code: 0,
@ -1717,10 +1723,14 @@ impl Editor {
Ok(doc_id)
}
pub fn document_id_by_path(&self, path: &Path) -> Option<DocumentId> {
self.document_by_path(path).map(|doc| doc.id)
}
// ??? possible use for integration tests
pub fn open(&mut self, path: &Path, action: Action) -> Result<DocumentId, DocumentOpenError> {
let path = helix_stdx::path::canonicalize(path);
let id = self.document_by_path(&path).map(|doc| doc.id);
let id = self.document_id_by_path(&path);
let id = if let Some(id) = id {
id

View file

@ -162,7 +162,12 @@ pub(crate) mod keys {
impl fmt::Display for KeyEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{}{}{}",
"{}{}{}{}",
if self.modifiers.contains(KeyModifiers::SUPER) {
"Meta-"
} else {
""
},
if self.modifiers.contains(KeyModifiers::SHIFT) {
"S-"
} else {
@ -312,6 +317,10 @@ impl UnicodeWidthStr for KeyEvent {
if self.modifiers.contains(KeyModifiers::CONTROL) {
width += 2;
}
if self.modifiers.contains(KeyModifiers::SUPER) {
// "-Meta"
width += 5;
}
width
}
@ -387,6 +396,23 @@ impl std::str::FromStr for KeyEvent {
.then_some(KeyCode::F(function))
.ok_or_else(|| anyhow!("Invalid function key '{}'", function))?
}
// Checking that the last token is empty ensures that this branch is only taken if
// `-` is used as a code. For example this branch will not be taken for `S-` (which is
// missing a code).
_ if s.ends_with('-') && tokens.last().is_some_and(|t| t.is_empty()) => {
if s == "-" {
return Ok(KeyEvent {
code: KeyCode::Char('-'),
modifiers: KeyModifiers::empty(),
});
} else {
let suggestion = format!("{}-{}", s.trim_end_matches('-'), keys::MINUS);
return Err(anyhow!(
"Key '-' cannot be used with modifiers, use '{}' instead",
suggestion
));
}
}
invalid => return Err(anyhow!("Invalid key code '{}'", invalid)),
};
@ -396,6 +422,7 @@ impl std::str::FromStr for KeyEvent {
"S" => KeyModifiers::SHIFT,
"A" => KeyModifiers::ALT,
"C" => KeyModifiers::CONTROL,
"Meta" | "Cmd" | "Win" => KeyModifiers::SUPER,
_ => return Err(anyhow!("Invalid key modifier '{}-'", token)),
};
@ -661,6 +688,13 @@ mod test {
modifiers: KeyModifiers::NONE
}
);
assert_eq!(
str::parse::<KeyEvent>("-").unwrap(),
KeyEvent {
code: KeyCode::Char('-'),
modifiers: KeyModifiers::NONE,
}
);
}
#[test]
@ -709,6 +743,28 @@ mod test {
modifiers: KeyModifiers::NONE
}
);
assert_eq!(
str::parse::<KeyEvent>("Meta-c").unwrap(),
KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::SUPER
}
);
assert_eq!(
str::parse::<KeyEvent>("Win-s").unwrap(),
KeyEvent {
code: KeyCode::Char('s'),
modifiers: KeyModifiers::SUPER
}
);
assert_eq!(
str::parse::<KeyEvent>("Cmd-d").unwrap(),
KeyEvent {
code: KeyCode::Char('d'),
modifiers: KeyModifiers::SUPER
}
);
}
#[test]
@ -721,6 +777,7 @@ mod test {
assert!(str::parse::<KeyEvent>("FU").is_err());
assert!(str::parse::<KeyEvent>("123").is_err());
assert!(str::parse::<KeyEvent>("S--").is_err());
assert!(str::parse::<KeyEvent>("S-").is_err());
assert!(str::parse::<KeyEvent>("S-percent").is_err());
}

View file

@ -7,6 +7,7 @@ bitflags! {
const SHIFT = 0b0000_0001;
const CONTROL = 0b0000_0010;
const ALT = 0b0000_0100;
const SUPER = 0b0000_1000;
const NONE = 0b0000_0000;
}
}
@ -27,6 +28,9 @@ impl From<KeyModifiers> for crossterm::event::KeyModifiers {
if key_modifiers.contains(KeyModifiers::ALT) {
result.insert(CKeyModifiers::ALT);
}
if key_modifiers.contains(KeyModifiers::SUPER) {
result.insert(CKeyModifiers::SUPER);
}
result
}
@ -48,6 +52,9 @@ impl From<crossterm::event::KeyModifiers> for KeyModifiers {
if val.contains(CKeyModifiers::ALT) {
result.insert(KeyModifiers::ALT);
}
if val.contains(CKeyModifiers::SUPER) {
result.insert(KeyModifiers::SUPER);
}
result
}

View file

@ -5,7 +5,7 @@ use arc_swap::access::DynAccess;
use helix_core::NATIVE_LINE_ENDING;
use crate::{
clipboard::{ClipboardProvider, ClipboardType},
clipboard::{ClipboardError, ClipboardProvider, ClipboardType},
Editor,
};
@ -238,6 +238,10 @@ fn read_from_clipboard<'a>(
RegisterValues::new(iter::once(contents.into()))
}
}
Err(ClipboardError::ReadingNotSupported) => match saved_values {
Some(values) => RegisterValues::new(values.iter().map(Cow::from).rev()),
None => RegisterValues::new(iter::empty()),
},
Err(err) => {
log::error!(
"Failed to read {} clipboard: {err}",
@ -307,13 +311,13 @@ impl<'a> Iterator for RegisterValues<'a> {
}
}
impl<'a> DoubleEndedIterator for RegisterValues<'a> {
impl DoubleEndedIterator for RegisterValues<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back()
}
}
impl<'a> ExactSizeIterator for RegisterValues<'a> {
impl ExactSizeIterator for RegisterValues<'_> {
fn len(&self) -> usize {
self.iter.len()
}

View file

@ -280,7 +280,7 @@ fn build_theme_values(
for (name, style_value) in values {
let mut style = Style::default();
if let Err(err) = palette.parse_style(&mut style, style_value) {
warnings.push(err);
warnings.push(format!("Failed to parse style for key {name:?}. {err}"));
}
// these are used both as UI and as highlights

View file

@ -705,7 +705,7 @@ impl<'a> Iterator for Traverse<'a> {
}
}
impl<'a> DoubleEndedIterator for Traverse<'a> {
impl DoubleEndedIterator for Traverse<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
loop {
let key = self.stack.pop()?;

View file

@ -62,7 +62,7 @@ marksman = { command = "marksman", args = ["server"] }
metals = { command = "metals", config = { "isHttpEnabled" = true, metals = { inlayHints = { typeParameters = {enable = true} , hintsInPatternMatch = {enable = true} } } } }
mesonlsp = { command = "mesonlsp", args = ["--lsp"] }
mint = { command = "mint", args = ["ls"] }
mojo-lsp = { command = "mojo-lsp-server" }
mojo-lsp = { command = "magic", args = ["run", "mojo-lsp-server"] }
nil = { command = "nil" }
nimlangserver = { command = "nimlangserver" }
nimlsp = { command = "nimlsp" }
@ -123,6 +123,7 @@ tinymist = { command = "tinymist" }
pkgbuild-language-server = { command = "pkgbuild-language-server" }
helm_ls = { command = "helm_ls", args = ["serve"] }
ember-language-server = { command = "ember-language-server", args = ["--stdio"] }
teal-language-server = { command = "teal-language-server" }
[language-server.ansible-language-server]
command = "ansible-language-server"
@ -411,7 +412,7 @@ language-servers = [ "mojo-lsp" ]
comment-token = "#"
indent = { tab-width = 4, unit = " " }
auto-format = true
formatter = { command = "mojo", args = ["format", "-q", "-"]}
formatter = { command = "magic", args = ["run", "mojo" , "format", "-q", "-"]}
[[grammar]]
name = "mojo"
@ -1297,6 +1298,20 @@ language-servers = [ "lua-language-server" ]
name = "lua"
source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-lua", rev = "88e446476a1e97a8724dff7a23e2d709855077f2" }
[[grammar]]
name = "teal"
source = { git = "https://github.com/euclidianAce/tree-sitter-teal", rev = "3db655924b2ff1c54fdf6371b5425ea6b5dccefe" }
[[language]]
name = "teal"
scope = "source.tl"
injection-regex = "teal"
file-types = ["tl"]
comment-tokens = "--"
block-comment-tokens = { start = "--[[", end = "--]]" }
roots = [ "tlconfig.lua" ]
language-servers = [ "teal-lsp" ]
[[language]]
name = "svelte"
scope = "source.svelte"
@ -2271,7 +2286,7 @@ indent = { tab-width = 4, unit = "\t" }
[[grammar]]
name = "v"
source = {git = "https://github.com/v-analyzer/v-analyzer", subpath = "tree_sitter_v", rev = "e14fdf6e661b10edccc744102e4ccf0b187aa8ad"}
source = {git = "https://github.com/vlang/v-analyzer", subpath = "tree_sitter_v", rev = "e14fdf6e661b10edccc744102e4ccf0b187aa8ad"}
[[language]]
name = "verilog"
@ -2540,11 +2555,11 @@ source = { git = "https://github.com/sourcegraph/tree-sitter-jsonnet", rev = "04
name = "ada"
scope = "source.ada"
injection-regex = "ada"
file-types = ["adb", "ads", "gpr"]
file-types = ["adb", "ads"]
roots = ["alire.toml"]
comment-token = "--"
indent = { tab-width = 3, unit = " " }
language-servers = ["ada-language-server", "ada-gpr-language-server"]
language-servers = ["ada-language-server"]
[[grammar]]
@ -3168,7 +3183,7 @@ indent = { tab-width = 4, unit = " " }
[[grammar]]
name = "just"
source = { git = "https://github.com/poliorcetics/tree-sitter-just", rev = "6e28fa6cba511c694247cd802d1c3b14f8d34dbb" }
source = { git = "https://github.com/poliorcetics/tree-sitter-just", rev = "180bb15d64c63585c4f65c551350048f21987bcb" }
[[language]]
name = "gn"
@ -3463,7 +3478,11 @@ source = { git = "https://github.com/urbit-pilled/tree-sitter-hoon", rev = "1d5d
[[language]]
name = "hocon"
scope = "source.conf"
file-types = ["conf"]
file-types = [
{ glob = "**/src/*/resources/**/*.conf" },
{ glob = "*scalafmt*.conf" },
{ glob = "*scalafix*.conf" },
]
comment-token = "#"
auto-format = true
indent = { tab-width = 2, unit = " " }
@ -3942,3 +3961,28 @@ indent = { tab-width = 4, unit = " " }
[[grammar]]
name = "amber"
source = { git = "https://github.com/amber-lang/tree-sitter-amber", rev = "c6df3ec2ec243ed76550c525e7ac3d9a10c6c814" }
[[language]]
name = "gpr"
scope = "source.gpr"
injection-regex = "gpr"
file-types = ["gpr"]
roots = ["alire.toml"]
comment-token = "--"
indent = { tab-width = 3, unit = " " }
language-servers = ["ada-gpr-language-server"]
[[grammar]]
name = "gpr"
source = { git = "https://github.com/brownts/tree-sitter-gpr", rev = "cea857d3c18d1385d1f5b66cd09ea1e44173945c" }
[[language]]
name = "vento"
scope = "text.html.vto"
file-types = ["vto"]
block-comment-tokens = { start = "{{#", end = "#}}" }
indent = { tab-width = 4, unit = " " }
[[grammar]]
name = "vento"
source = { git = "https://github.com/ventojs/tree-sitter-vento", rev = "3321077d7446c1b3b017c294fd56ce028ed817fe" }

24
pr.md
View file

@ -1,24 +0,0 @@
Syntax symbol pickers
==
This adds two new symbol picker commands that use tree-sitter rather than LSP. We run a new `symbols.scm` query across the file and extract tagged things like function definitions, types, classes, etc. For languages with unambiguous syntax this behaves roughly the same as the LSP symbol picker (`<space>s`). It's less precise though since we don't have semantic info about the language. For example it can easily produce false positives for C/C++ because of preprocessor magic.
The hope is to start introducing LSP-like features for navigation that can work without installing or running a language server. I made these two pickers in particular because I don't like LSP equivalents in ErlangLS - the document symbol picker can take a long time to show up during boot and the workspace symbol picker only searches for module names. The other motivation is to have some navigation features in cases when running a language server is too cumbersome - either to set up or because of resource constraints. For example `clangd` needs a fair amount of setup (`compile_commands.json`) that you might not want to do when quickly reading through a codebase.
GitHub already uses tree-sitter like this to provide [imprecise code navigation](https://docs.github.com/en/repositories/working-with-files/using-files/navigating-code-on-github#about-navigating-code-on-github). It should be possible to find definitions and references as well like `gd` and `gr` - this is left as a follow-up.
This PR also adds commands that either open the LSP symbol picker or the syntax one if a language server is not available. This way you can customize a language to not use the LSP symbol pickers, for example:
```toml
[[language]]
name = "erlang"
language-servers = [{ name = "erlang-ls", except-features = ["document-symbols", "workspace-symbols"] }]
```
and `<space>s` will use the syntax symbol picker, while `<space>s` on a Rust file will still prefer the language server.
---
Outstanding question - how closely should we try to match LSP symbol kind? Not at all? Should we have markup specific symbol kinds? (For example see markdown's `symbols.scm`).
Also this PR needs docs on writing `symbols.scm` queries.

View file

@ -21,10 +21,10 @@
; Error level tags
((tag (name) @error)
(#match? @error "^(BUG|FIXME|ISSUE|XXX|FIX|SAFETY|FIXIT|FAILED|DEBUG|INVARIANT)$"))
(#match? @error "^(BUG|FIXME|ISSUE|XXX|FIX|SAFETY|FIXIT|FAILED|DEBUG|INVARIANT|COMPLIANCE)$"))
("text" @error
(#match? @error "^(BUG|FIXME|ISSUE|XXX|FIX|SAFETY|FIXIT|FAILED|DEBUG|INVARIANT)$"))
(#match? @error "^(BUG|FIXME|ISSUE|XXX|FIX|SAFETY|FIXIT|FAILED|DEBUG|INVARIANT|COMPLIANCE)$"))
(tag
(name) @ui.text

View file

@ -0,0 +1,51 @@
[ "abstract" "all" "at"
"case"
"end" "extends" "external" "external_as_list"
"for"
"is"
"limited"
"null"
"others"
"package"
;; "project"
"renames"
"type"
"use"
"when"
"with"
] @keyword
;; Avoid highlighting Project in Project'Project_Dir
(project_declaration "project" @keyword)
;; highlight qualifiers as keywords (not all qualifiers are actual keywords)
(project_qualifier _ @keyword)
[":=" "&" "|" "=>"] @operator
(comment) @comment
(string_literal) @string
(numeric_literal) @constant.numeric
;; Type
(typed_string_declaration name: (identifier) @type)
(variable_declaration type: (name (identifier) @type .))
;; Variable
(variable_declaration name: (identifier) @variable)
(variable_reference (name (identifier) @variable .) .)
;; Function
(builtin_function_call name: _ @function.builtin)
;; Attribute
(attribute_declaration name: (identifier) @attribute)
(attribute_reference (identifier) @attribute)
;; Package
(variable_reference (name (identifier) @function .) "'")
(package_declaration
[ name: (identifier) @function
endname: (identifier) @function
origname: (name (identifier) @function .)
basename: (name (identifier) @function .)])

View file

@ -3,6 +3,7 @@
[
"export"
"import"
"unexport"
] @keyword.control.import
"mod" @keyword.directive
@ -18,6 +19,11 @@
"else"
] @keyword.control.conditional
[
"&&"
"||"
] @operator
; Variables
(value
@ -31,6 +37,9 @@
(shell_variable_name) @variable
(unexport
name: (identifier) @variable)
; Functions
(recipe

View file

@ -0,0 +1,15 @@
[
(do_statement)
(numeric_for_statement)
(generic_for_statement)
(while_statement)
(repeat_statement)
(if_statement)
(function_statement)
(record_declaration)
(interface_declaration)
(enum_declaration)
(anon_function)
(table_constructor)
] @fold

View file

@ -0,0 +1,170 @@
;; Primitives
(boolean) @constant.builtin.boolean
(comment) @comment
(shebang_comment) @comment
(identifier) @variable
((identifier) @variable.builtin
(#eq? @variable.builtin "self"))
(nil) @constant.builtin
(number) @constant.numeric
(string) @string
(table_constructor ["{" "}"] @constructor)
(varargs "..." @constant.builtin)
[ "," "." ":" ";" ] @punctuation.delimiter
(escape_sequence) @constant.character.escape
(format_specifier) @constant.character.escape
;; Basic statements/Keywords
[ "if" "then" "elseif" "else" ] @keyword.control.conditional
[ "for" "while" "repeat" "until" "do" ] @keyword.control.repeat
[ "end" ] @keyword
[ "in" ] @keyword.operator
[ "local" ] @keyword.storage.type
[ (break) (goto) ] @keyword.control
[ "return" ] @keyword.control.return
(label) @label
;; Global isn't a real keyword, but it gets special treatment in these places
(var_declaration "global" @keyword.storage.type)
(type_declaration "global" @keyword.storage.type)
(function_statement "global" @keyword.storage.type)
(record_declaration "global" @keyword.storage.type)
(interface_declaration "global" @keyword.storage.type)
(enum_declaration "global" @keyword.storage.type)
(macroexp_statement "macroexp" @keyword)
;; Ops
(bin_op (op) @operator)
(unary_op (op) @operator)
[ "=" "as" ] @operator
;; Functions
(function_statement
"function" @keyword.function
. name: (_) @function)
(anon_function
"function" @keyword.function)
(function_body "end" @keyword.function)
(arg name: (identifier) @variable.parameter)
(function_signature
(arguments
. (arg name: (identifier) @variable.builtin))
(#eq? @variable.builtin "self"))
(typeargs
"<" @punctuation.bracket
. (_) @type.parameter
. ("," . (_) @type.parameter)*
. ">" @punctuation.bracket)
(function_call
(identifier) @function . (arguments))
(function_call
(index (_) key: (identifier) @function) . (arguments))
(function_call
(method_index (_) key: (identifier) @function) . (arguments))
;; Types
; Contextual keywords in record bodies
(record_declaration
. [ "record" ] @keyword.storage.type
name: (identifier) @type)
(anon_record . "record" @keyword.storage.type)
(record_body
(record_declaration
. [ "record" ] @keyword.storage.type
. name: (identifier) @type))
(record_body
(enum_declaration
. [ "enum" ] @keyword.storage.type
. name: (identifier) @type.enum))
(record_body
(interface_declaration
. [ "interface" ] @keyword.storage.type
. name: (identifier) @type))
(record_body
(typedef
. "type" @keyword.storage.type
. name: (identifier) @type . "="))
(record_body
(macroexp_declaration
. [ "macroexp" ] @keyword.storage.type))
(record_body (metamethod "metamethod" @keyword.storage.modifier))
(record_body (userdata) @keyword.storage.modifier)
; Contextual keywords in interface bodies
(interface_declaration
. [ "interface" ] @keyword.storage.type
name: (identifier) @type)
(anon_interface . "interface" @keyword.storage.type)
(interface_body
(record_declaration
. [ "record" ] @keyword.storage.type
. name: (identifier) @type))
(interface_body
(enum_declaration
. [ "enum" ] @keyword.storage.type
. name: (identifier) @type.enum))
(interface_body
(interface_declaration
. [ "interface" ] @keyword.storage.type
. name: (identifier) @type))
(interface_body
(typedef
. "type" @keyword.storage.type
. name: (identifier) @type . "="))
(interface_body
(macroexp_declaration
. [ "macroexp" ] @keyword.storage.type))
(interface_body (metamethod "metamethod" @keyword.storage.modifier))
(interface_body (userdata) @keyword.storage.modifier)
(enum_declaration
"enum" @keyword.storage.type
name: (identifier) @type.enum)
(type_declaration "type" @keyword.storage.type)
(type_declaration (identifier) @type)
(simple_type) @type
(type_index) @type
(type_union "|" @operator)
(function_type "function" @type)
;; The rest of it
(var_declaration
declarators: (var_declarators
(var name: (identifier) @variable)))
(var_declaration
declarators: (var_declarators
(var
"<" @punctuation.bracket
. attribute: (attribute) @attribute
. ">" @punctuation.bracket)))
[ "(" ")" "[" "]" "{" "}" ] @punctuation.bracket
;; Only highlight format specifiers in calls to string.format
;; string.format('...')
;(function_call
; called_object: (index
; (identifier) @base
; key: (identifier) @entry)
; arguments: (arguments .
; (string (format_specifier) @string.escape))
;
; (#eq? @base "string")
; (#eq? @entry "format"))
;; ('...'):format()
;(function_call
; called_object: (method_index
; (string (format_specifier) @string.escape)
; key: (identifier) @func-name)
; (#eq? @func-name "format"))

View file

@ -0,0 +1,25 @@
(var_declaration
declarators: (var_declarators
(var (identifier)) @local.definition))
(var_assignment
variables: (assignment_variables
(var (identifier) @local.definition)))
(arg name: (identifier) @local.definition)
(anon_function) @local.scope
((function_statement
(function_name) @local.definition) @local.scope)
(program) @local.scope
(if_statement) @local.scope
(generic_for_statement (for_body) @local.scope)
(numeric_for_statement (for_body) @local.scope)
(repeat_statement) @local.scope
(while_statement (while_body) @local.scope)
(do_statement) @local.scope
(identifier) @local.reference

View file

@ -0,0 +1,13 @@
(comment) @comment
(keyword) @keyword
(tag
[
"{{"
"{{-"
"}}"
"-}}"
] @punctuation.bracket)
"|>" @operator

View file

@ -0,0 +1,6 @@
((content) @injection.content
(#set! injection.language "html")
(#set! injection.combined))
((code) @injection.content
(#set! injection.language "javascript"))

View file

@ -52,6 +52,8 @@
"markup.list" = "mauve"
"markup.bold" = { modifiers = ["bold"] }
"markup.italic" = { modifiers = ["italic"] }
"markup.list.unchecked" = "overlay2"
"markup.list.checked" = "green"
"markup.link.url" = { fg = "blue", modifiers = ["italic", "underlined"] }
"markup.link.text" = "blue"
"markup.raw" = "flamingo"

View file

@ -26,6 +26,8 @@
"ui.menu.selected" = { bg = "ui", fg = "tx" }
"ui.debug" = { fg = "or", bg = "bg" }
"ui.highlight.frameline" = { bg = "ye" }
"ui.bufferline" = { fg = "tx-2", bg = "bg-2"}
"ui.bufferline.active" = { fg = "ye", bg = "bg-2" }
"diagnostic.hint" = { underline = { color = "bl", style = "curl" } }
"diagnostic.info" = { underline = { color = "bl", style = "curl" } }
"diagnostic.warning" = { underline = { color = "ye", style = "curl" } }
@ -36,7 +38,6 @@
"info" = { fg = "ye", modifiers = ["bold"] }
"warning" = { fg = "or", modifiers = ["bold"] }
"error" = { fg = "re", modifiers = ["bold"] }
"attribute" = "ye"
"type" = "ye"
"constructor" = "gr"
@ -62,7 +63,6 @@
"function" = "or"
"tag" = "bl"
"namespace" = "re"
"markup.heading" = "or"
"markup.list" = "ye"
"markup.bold" = { fg = "or", modifiers = ["bold"] }
@ -88,6 +88,7 @@ ui = "#E6E4D9"
bg-2 = "#F2F0E5"
bg = "#FFFCF0"
re = "#AF3029"
or = "#BC5215"
ye = "#AD8301"

102
runtime/themes/vintage.toml Normal file
View file

@ -0,0 +1,102 @@
# Vintage Theme for the Helix Editor
# Author: rojebd<roniellberrios@gmail.com>
# Repo: https://github.com/rojebd/vintage
# This theme is vintage inspired
# Info: I made this theme one afternoon because my bus was late and I stayed home :)
attribute = "#a7bf67"
keyword = "#A4A2B4"
"keyword.directive" = "light-gray"
namespace = "#7095bf"
punctuation = "white"
"punctuation.delimeter" = "white"
operator = "muddy"
special = "pink"
"variable.other.member" = "green"
variable = "green"
"variable.parameter" = "light-green"
"variable.builtin" = "light-green"
type = "#efbe4c"
"type.builtin" = "#efbe4c"
constructor = "#c19ef7"
function = "muddy"
"function.macro" = "muddy"
"function.builtin" = "#db985e"
tag = "#d37a78"
comment = "light-gray"
constant = "#A5C4D4"
"constant.builtin" = "bright-yellow"
string = "#d6a560"
"constant.numeric" = "#b577b0"
"constant.character.escape" = "#c95c56"
label = "#abcc8a"
"markup.heading" = "bright-pink"
"markup.bold" = { fg = "markdown-bold", modifiers = ["bold"] }
"markup.italic" = { fg = "markdown-italic", modifiers = ["italic"] }
"markup.strikethrough" = { fg = "bright-pink", modifiers = ["crossed_out", "bold"] }
"markup.link.url" = { fg = "blueish", modifiers = ["underlined"] }
"markup.link.text" = "orange"
"markup.raw" = "gray"
"diff.plus" = { fg = "bright-green", modifiers = ["bold"] }
"diff.minus" = { fg = "redish", modifiers = ["bold"] }
"diff.delta" = { fg = "blueish", modifiers = ["bold"] }
"ui.background" = { bg = "background" }
"ui.background.separator" = { fg = "white" }
"ui.linenr" = { fg = "#747575" }
"ui.linenr.selected" = { fg = "#c7dddd" }
"ui.statusline" = { fg = "black", bg = "gray" }
"ui.statusline.inactive" = { fg = "gray", bg = "#3c3836" }
"ui.popup" = { bg = "#3b3b3d" }
"ui.window" = { fg = "yellow" }
"ui.help" = { bg = "#35353a", fg = "light-gray" }
"ui.text" = { fg = "light-gray" }
"ui.text.focus" = { fg = "#83c679" }
"ui.text.inactive" = "#93a56f"
"ui.virtual" = { fg = "yellow" }
"ui.virtual.indent-guide" = { fg = "#5b5b5a" }
"ui.virtual.ruler" = { bg = "#363638" }
"ui.selection" = { fg = "white", bg = "gray" }
"ui.selection.primary" = { fg = "white", bg = "gray" }
"ui.cursor.match" = { fg = "white", bg = "gray" }
"ui.cursor" = { modifiers = ["reversed"] }
"ui.cursorline.primary" = { bg = "#3b3b3f" }
"ui.debug" = { fg = "#634450" }
"ui.debug.breakpoint" = { fg = "redish" }
"ui.debug.active" = { fg = "blueish" }
"ui.menu" = { fg = "gray", bg = "black" }
"ui.menu.selected" = { fg = "black", bg = "gray" }
"diagnostic.hint" = { underline = { color = "bright-yellow", style = "curl" } }
"diagnostic.info" = { underline = { color = "bright-yellow", style = "curl" } }
"diagnostic.warning" = { underline = { color = "bright-yellow", style = "curl" } }
"diagnostic.error" = { underline = { color = "bright-pink", style = "curl" } }
warning = "bright-yellow"
error = "bright-pink"
info = "bright-yellow"
hint = "bright-yellow"
[palette]
bright-pink = "#DE6468"
background = "#1f1f21"
green = "#778e61"
light-green = "#9B9B7A"
brown = "#BAA587"
pink = "#D9AE94"
yellow = "#F1DCA7"
bright-yellow = "#FFCB69"
orange = "#E8AC65"
muddy = "#D08C60"
markdown-italic = "#80a552"
markdown-bold = "#6faa75"
dark-brown = "#B58463"
purple-ish = "#997B66"
bright-green = "#4F6F52"
blueish = "#3876BF"
redish = "#B80000"

View file

@ -1,3 +1,9 @@
# Voxed theme for the Helix Editor
# Author: rojebd<roniellberrios@gmail.com>
# Repo: https://github.com/rojebd/voxed
# Version: 1.0
# Info: This is a theme I made one afternoon because I was bored
attribute = "buff"
keyword = "sglow"
"keyword.directive" = "defineish"
@ -43,6 +49,7 @@ label = "yellow"
"ui.background.separator" = { fg = "sglow" }
"ui.linenr" = { fg = "light-grey", modifiers = ["italic"] }
"ui.linenr.selected" = { fg = "bpink", modifiers = ["bold"] }
"ui.statusline" = { fg = "black", bg = "light-grey", modifiers = ["bold"] }
"ui.statusline.inactive" = { fg = "black", bg = "bgrey-two" }
"ui.popup" = { fg = "bgrey", bg = "#25262B" }
@ -54,7 +61,8 @@ label = "yellow"
"ui.text.inactive" = "bgrey"
"ui.virtual" = { fg = "blue" }
"ui.virtual.ruler" = { bg = "bgrey-two" }
"ui.virtual.indent-guide" = { fg = "bpink" }
"ui.virtual.indent-guide" = { fg = "gray" }
"ui.selection" = { bg = "maize" }
"ui.selection.primary" = { fg = "white", bg = "bgrey" }
@ -62,7 +70,8 @@ label = "yellow"
"ui.cursor.insert" = { bg = "white" }
"ui.cursor.match" = { fg = "#212121", bg = "#6C6999" }
"ui.cursor" = { bg = "bgrey-two", modifiers = ["reversed"] }
"ui.cursorline.primary" = { bg = "white" }
"ui.cursorline.primary" = { bg = "#44414c" }
"ui.highlight" = { bg = "white" }
"ui.highlight.frameline" = { bg = "#634450" }
"ui.debug" = { fg = "#634450" }
@ -75,8 +84,6 @@ label = "yellow"
"diagnostic.info" = { underline = { color = "sglow", style = "curl" } }
"diagnostic.warning" = { underline = { color = "redish", style = "curl" } }
"diagnostic.error" = { underline = { color = "bpink", style = "curl" } }
"diagnostic.unnecessary" = { modifiers = ["dim"] }
"diagnostic.deprecated" = { modifiers = ["crossed_out"] }
warning = "bpink"
error = "bsienna"

View file

@ -1,12 +1,18 @@
use crate::helpers;
use crate::path;
use crate::DynError;
use helix_term::commands::MappableCommand;
use helix_term::commands::TYPABLE_COMMAND_LIST;
use helix_term::health::TsFeature;
use helix_term::ui::EditorView;
use helix_view::document::Mode;
use std::collections::HashSet;
use std::fs;
pub const TYPABLE_COMMANDS_MD_OUTPUT: &str = "typable-cmd.md";
pub const STATIC_COMMANDS_MD_OUTPUT: &str = "static-cmd.md";
pub const LANG_SUPPORT_MD_OUTPUT: &str = "lang-support.md";
fn md_table_heading(cols: &[String]) -> String {
@ -48,6 +54,68 @@ pub fn typable_commands() -> Result<String, DynError> {
Ok(md)
}
pub fn static_commands() -> Result<String, DynError> {
let mut md = String::new();
let keymap = EditorView::default().keymaps.map();
let keymaps = [
("normal", keymap[&Mode::Normal].reverse_map()),
("select", keymap[&Mode::Select].reverse_map()),
("insert", keymap[&Mode::Insert].reverse_map()),
];
md.push_str(&md_table_heading(&[
"Name".to_owned(),
"Description".to_owned(),
"Default keybinds".to_owned(),
]));
for cmd in MappableCommand::STATIC_COMMAND_LIST {
let keymap_strings: Vec<_> = keymaps
.iter()
.map(|(mode, keymap)| {
let bindings = keymap
.get(cmd.name())
.map(|bindings| {
let mut bind_strings: Vec<_> = bindings
.iter()
.map(|bind| {
let keys = &bind
.iter()
.map(|key| key.key_sequence_format())
.collect::<String>()
// escape | so it doesn't get rendered as a column separator
.replace('|', "\\|");
format!("`` {} ``", keys)
})
.collect();
// sort for stable output. sorting by length puts simple
// keybindings first and groups similar keys together
bind_strings.sort_by_key(|s| (s.len(), s.to_owned()));
bind_strings.join(", ")
})
.unwrap_or_default();
(mode, bindings)
})
.collect();
let keymap_string = keymap_strings
.iter()
.filter(|(_, bindings)| !bindings.is_empty())
.map(|(mode, bindings)| format!("{}: {}", mode, bindings))
.collect::<Vec<_>>()
.join(", ");
md.push_str(&md_table_row(&[
md_mono(cmd.name()),
cmd.doc().to_owned(),
keymap_string,
]));
}
Ok(md)
}
pub fn lang_features() -> Result<String, DynError> {
let mut md = String::new();
let ts_features = TsFeature::all();
@ -59,7 +127,7 @@ pub fn lang_features() -> Result<String, DynError> {
.map(|t| t.long_title().to_string())
.collect::<Vec<_>>(),
);
cols.push("Default LSP".to_owned());
cols.push("Default language servers".to_owned());
md.push_str(&md_table_heading(&cols));
let config = helpers::lang_config();

View file

@ -9,14 +9,17 @@ use std::{env, error::Error};
type DynError = Box<dyn Error>;
pub mod tasks {
use crate::docgen::{lang_features, typable_commands, write};
use crate::docgen::{LANG_SUPPORT_MD_OUTPUT, TYPABLE_COMMANDS_MD_OUTPUT};
use crate::docgen::{lang_features, static_commands, typable_commands, write};
use crate::docgen::{
LANG_SUPPORT_MD_OUTPUT, STATIC_COMMANDS_MD_OUTPUT, TYPABLE_COMMANDS_MD_OUTPUT,
};
use crate::querycheck::query_check;
use crate::theme_check::theme_check;
use crate::DynError;
pub fn docgen() -> Result<(), DynError> {
write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?);
write(STATIC_COMMANDS_MD_OUTPUT, &static_commands()?);
write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?);
Ok(())
}