mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-10 01:21:16 -03:00
Compare commits
395 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3478f78a05 | ||
|
|
19cedb01bc | ||
|
|
cb24c4a863 | ||
|
|
93478e7c51 | ||
|
|
4eac5f4d9d | ||
|
|
e76370b3b7 | ||
|
|
1e3153c3fb | ||
|
|
d0a95e4fde | ||
|
|
7174ebbb4b | ||
|
|
e81cec1633 | ||
|
|
02c04550fd | ||
|
|
b0bfc7758a | ||
|
|
cbf21b13d7 | ||
|
|
14f3c3e13e | ||
|
|
05ea6e9e66 | ||
|
|
74d2e6d5d8 | ||
|
|
1b8f9c2b03 | ||
|
|
491158dfad | ||
|
|
7fa08e31c6 | ||
|
|
7ac9ce7ffb | ||
|
|
197779697d | ||
|
|
96fabe4b29 | ||
|
|
4a4d35b625 | ||
|
|
92739a06e2 | ||
|
|
244e9586ca | ||
|
|
13a2ccae66 | ||
|
|
ed34845b10 | ||
|
|
74b104c9f6 | ||
|
|
1f8cdf85b6 | ||
|
|
dc02a8ce35 | ||
|
|
65e726324a | ||
|
|
fe3c42af9e | ||
|
|
28c7e7173f | ||
|
|
a897a26daa | ||
|
|
ffce214362 | ||
|
|
bdb8e4847f | ||
|
|
9750d8c3ad | ||
|
|
dadec16661 | ||
|
|
b5cd2763db | ||
|
|
a08dd70354 | ||
|
|
5b39d1fc6a | ||
|
|
38513de954 | ||
|
|
cc1ae25c3a | ||
|
|
002fa0e791 | ||
|
|
0589de7523 | ||
|
|
4e7e0139fd | ||
|
|
c044e1d433 | ||
|
|
fb0edf564e | ||
|
|
969ce68d5d | ||
|
|
6ef2e04518 | ||
|
|
bc84fe9407 | ||
|
|
38e8416da5 | ||
|
|
cf0e07aecd | ||
|
|
8e014bbf97 | ||
|
|
f9013ad7a6 | ||
|
|
8d2d50573a | ||
|
|
d25965afba | ||
|
|
6f895935a9 | ||
|
|
70ebc969f9 | ||
|
|
6d01138797 | ||
|
|
779f1371a1 | ||
|
|
5d24a846e3 | ||
|
|
c4f1c25a87 | ||
|
|
7d754b2865 | ||
|
|
232e38f105 | ||
|
|
725afc71fd | ||
|
|
6d60eaf04e | ||
|
|
7c1d8cf4f1 | ||
|
|
e1a0df68fc | ||
|
|
14832117b6 | ||
|
|
56deaafb34 | ||
|
|
b8b36f3293 | ||
|
|
a9ab708494 | ||
|
|
a33363902c | ||
|
|
3ca3d10e28 | ||
|
|
05c236481f | ||
|
|
25e2bdb7de | ||
|
|
12bd4cf9e9 | ||
|
|
0d79681070 | ||
|
|
47d7f52149 | ||
|
|
000a5cc80a | ||
|
|
66f51f5166 | ||
|
|
7360deee74 | ||
|
|
e0916e793b | ||
|
|
f43a79bc13 | ||
|
|
fb56a6a54d | ||
|
|
f6936f222a | ||
|
|
553aa40b34 | ||
|
|
ac9c339e5a | ||
|
|
0ae3b33f2f | ||
|
|
6dbe5637ef | ||
|
|
09d8570922 | ||
|
|
4ab433418e | ||
|
|
73e3b47ebd | ||
|
|
bb7dce941a | ||
|
|
55e9255264 | ||
|
|
cf53ef02c4 | ||
|
|
c8ea418154 | ||
|
|
44920b4be8 | ||
|
|
b980e50acf | ||
|
|
e78e3f16e1 | ||
|
|
714c2f7373 | ||
|
|
f1036beb1f | ||
|
|
bd4e55b149 | ||
|
|
22e5e63a65 | ||
|
|
414dba994f | ||
|
|
934def3b75 | ||
|
|
74c2837d87 | ||
|
|
40c480c2cf | ||
|
|
ad82c7ebf1 | ||
|
|
86b126628c | ||
|
|
24ee3afdae | ||
|
|
08a9f5e683 | ||
|
|
ffb09a429d | ||
|
|
87048330b7 | ||
|
|
c2f7c5fbeb | ||
|
|
4fde7e4f2f | ||
|
|
3369cf9a70 | ||
|
|
ff5e83cd42 | ||
|
|
193a564c8f | ||
|
|
296e2369d7 | ||
|
|
16cb7cc550 | ||
|
|
c86b8c8130 | ||
|
|
1b1badfd02 | ||
|
|
b7f990fe4a | ||
|
|
e639efb77c | ||
|
|
41c2f8b9e0 | ||
|
|
78b33eb47b | ||
|
|
7e1164762c | ||
|
|
8a9203e8e3 | ||
|
|
99cae405ca | ||
|
|
f11c9c5105 | ||
|
|
f8f98d2581 | ||
|
|
e7648662ae | ||
|
|
69c4021646 | ||
|
|
d0084cb9df | ||
|
|
5f59582e63 | ||
|
|
c89c516467 | ||
|
|
40bf0e0ce9 | ||
|
|
07e3e924c3 | ||
|
|
f2bb5b5d7f | ||
|
|
3bd6771ae7 | ||
|
|
527176ca97 | ||
|
|
0cd8d64607 | ||
|
|
68e408b930 | ||
|
|
74b03de449 | ||
|
|
ac1c77d499 | ||
|
|
c7aaf4249a | ||
|
|
11dda63f7b | ||
|
|
04b799c0c5 | ||
|
|
44f9363e39 | ||
|
|
43513d3fca | ||
|
|
e41f2f6588 | ||
|
|
17d870a842 | ||
|
|
ab02558181 | ||
|
|
fa1a128aeb | ||
|
|
1f0a7e7697 | ||
|
|
cce788388f | ||
|
|
a93c24b084 | ||
|
|
6a2f531f9b | ||
|
|
eda1694581 | ||
|
|
1854d03b95 | ||
|
|
2180777f73 | ||
|
|
f2fc779c94 | ||
|
|
3a024b6d8f | ||
|
|
33d9957bcb | ||
|
|
28c0a8bc81 | ||
|
|
77471c500e | ||
|
|
98eaef2778 | ||
|
|
78f4541116 | ||
|
|
2a3fe73a6d | ||
|
|
9c3cb154d3 | ||
|
|
4e68a36141 | ||
|
|
b62fa06753 | ||
|
|
17129f64db | ||
|
|
524c73d8b7 | ||
|
|
78e91e6bc4 | ||
|
|
9d260cac0b | ||
|
|
01d91dd65c | ||
|
|
69d96e1e66 | ||
|
|
1ab12dadac | ||
|
|
a7dc701f26 | ||
|
|
590ad9cd93 | ||
|
|
cbca5177ea | ||
|
|
e3105bee39 | ||
|
|
0778919e7f | ||
|
|
e36684786f | ||
|
|
8fcd6524db | ||
|
|
23fb01f921 | ||
|
|
14915108d1 | ||
|
|
20cc07c5cd | ||
|
|
2f74785620 | ||
|
|
03894fea3c | ||
|
|
f407ca18a4 | ||
|
|
07394a1621 | ||
|
|
8bf416cd64 | ||
|
|
1d2a5997cc | ||
|
|
53e6758cc3 | ||
|
|
2df1fa90f1 | ||
|
|
a454d53c28 | ||
|
|
6d5948d814 | ||
|
|
18061ad177 | ||
|
|
8eb59bc500 | ||
|
|
ddb3046ccd | ||
|
|
5e3bc86268 | ||
|
|
9dfb5705c5 | ||
|
|
66ea0f78be | ||
|
|
c99c84558f | ||
|
|
d35edcf5fa | ||
|
|
8718d62b11 | ||
|
|
939fa25a5b | ||
|
|
93eb9ee4d8 | ||
|
|
154c095e66 | ||
|
|
50a97856dc | ||
|
|
02cb93311a | ||
|
|
49b3721b75 | ||
|
|
db18515aed | ||
|
|
2708191d3c | ||
|
|
16612c6e49 | ||
|
|
40d45ee21e | ||
|
|
3df3b52b18 | ||
|
|
efbf8b0203 | ||
|
|
c84c006f42 | ||
|
|
97e0eda477 | ||
|
|
f1d78103e4 | ||
|
|
7072eec225 | ||
|
|
9b1cda9025 | ||
|
|
2c06731a14 | ||
|
|
b14692cb95 | ||
|
|
253f6726c0 | ||
|
|
36db3b7f3f | ||
|
|
385cdef89b | ||
|
|
72870d8331 | ||
|
|
081c469f6f | ||
|
|
1affa7a16e | ||
|
|
701c5da823 | ||
|
|
860f75ee97 | ||
|
|
334710ebf7 | ||
|
|
b15da3e118 | ||
|
|
f1a9b9bf43 | ||
|
|
abffe983f8 | ||
|
|
b363e4afe7 | ||
|
|
b60582ff75 | ||
|
|
92dae88f62 | ||
|
|
f7d9c92820 | ||
|
|
860bba759d | ||
|
|
564ef66665 | ||
|
|
c8642efeb0 | ||
|
|
309bb778af | ||
|
|
36c340b40f | ||
|
|
a79c54be66 | ||
|
|
143c8c4ffd | ||
|
|
116bcdac28 | ||
|
|
4762d6a0a7 | ||
|
|
6e00deffd0 | ||
|
|
34f3e5ef23 | ||
|
|
b9dfbcee13 | ||
|
|
de2ac37c92 | ||
|
|
a194a557c5 | ||
|
|
8e34dc4cdb | ||
|
|
bbb2f0de8d | ||
|
|
4c3fcc7b16 | ||
|
|
f33fef3ca3 | ||
|
|
10537df82d | ||
|
|
8af6b07d07 | ||
|
|
c014c95e1b | ||
|
|
bd86f53b9f | ||
|
|
042117ee30 | ||
|
|
38e633d49b | ||
|
|
ce286010a8 | ||
|
|
ef18b6684b | ||
|
|
ab984f98ab | ||
|
|
cfadb2de36 | ||
|
|
3d0b378c40 | ||
|
|
aa56359834 | ||
|
|
a0a60c2d29 | ||
|
|
324223ddff | ||
|
|
58e7a50de8 | ||
|
|
739b82c34d | ||
|
|
b5bf9d17e3 | ||
|
|
917fb024ea | ||
|
|
7a05ea0f93 | ||
|
|
5d75c47fcc | ||
|
|
c98fd886fd | ||
|
|
94fdb36f6b | ||
|
|
d1a40ace7d | ||
|
|
dd4d69a288 | ||
|
|
e16ea8df11 | ||
|
|
80e1942980 | ||
|
|
99109278a6 | ||
|
|
f924a880c8 | ||
|
|
5683d26d24 | ||
|
|
740aef06df | ||
|
|
557f6d1743 | ||
|
|
8d257f5c57 | ||
|
|
d880a14b1a | ||
|
|
d4fcc00821 | ||
|
|
6d8bb292ec | ||
|
|
c638401469 | ||
|
|
5930574d8a | ||
|
|
fdef7c8689 | ||
|
|
5c36a1be1b | ||
|
|
14f747019b | ||
|
|
d7d5d2a9be | ||
|
|
750955171a | ||
|
|
36fd93215b | ||
|
|
6e7353170a | ||
|
|
62cc117c12 | ||
|
|
af00695383 | ||
|
|
85ac91eb2b | ||
|
|
d1ed582919 | ||
|
|
06a14c4a76 | ||
|
|
400d5281f4 | ||
|
|
50778670fb | ||
|
|
9037cd779d | ||
|
|
c23a4cbd9f | ||
|
|
5d8f7801f7 | ||
|
|
756134cf2b | ||
|
|
c16677fd6f | ||
|
|
13bc514aa6 | ||
|
|
1c3403825c | ||
|
|
6f1ac7c949 | ||
|
|
f5d3fd8a82 | ||
|
|
0a23a78523 | ||
|
|
725cf33f1a | ||
|
|
2d6db3f980 | ||
|
|
41b9584bb3 | ||
|
|
c915435417 | ||
|
|
afcde1222b | ||
|
|
a3cb512628 | ||
|
|
fc71ba07da | ||
|
|
9c867225ee | ||
|
|
972355e2fc | ||
|
|
8f4c80699f | ||
|
|
e79b00d9d1 | ||
|
|
2f6b1eaaf9 | ||
|
|
3546ffa3ef | ||
|
|
30f96860a7 | ||
|
|
41d50f1a71 | ||
|
|
1e9c80f34c | ||
|
|
b88d2ed812 | ||
|
|
92dd37d3c7 | ||
|
|
f24cc6a8fc | ||
|
|
3117a488ec | ||
|
|
185b91de13 | ||
|
|
e20024f0f0 | ||
|
|
501ec1905e | ||
|
|
7ebd2011ff | ||
|
|
8004f354aa | ||
|
|
77e1aead40 | ||
|
|
e48a88a4b3 | ||
|
|
1154d9f663 | ||
|
|
810a707069 | ||
|
|
7fa9e9bfb9 | ||
|
|
48b0e7e695 | ||
|
|
848fa57144 | ||
|
|
4101e831af | ||
|
|
eb803ba6a7 | ||
|
|
248a8e7c54 | ||
|
|
2fa8c8cd7f | ||
|
|
8d5f5586dc | ||
|
|
c5bc7bd5f9 | ||
|
|
2b3bd29588 | ||
|
|
0be3f9e57e | ||
|
|
2524ece2cc | ||
|
|
b975472828 | ||
|
|
20427ff1f6 | ||
|
|
5b3b825ab2 | ||
|
|
ccd3348eed | ||
|
|
845b9be1f5 | ||
|
|
400f2b130a | ||
|
|
362f7cedf6 | ||
|
|
2c959469f0 | ||
|
|
6c34bcf8f6 | ||
|
|
f510b62b7f | ||
|
|
b31387416d | ||
|
|
941a6cb434 | ||
|
|
931072f5d1 | ||
|
|
f4f9db73da | ||
|
|
9ef3f30c56 | ||
|
|
d19c927760 | ||
|
|
22e5b21f10 | ||
|
|
f0d2444769 | ||
|
|
7975060e4a | ||
|
|
354dc3d272 | ||
|
|
7640e95bd7 | ||
|
|
767115a93d | ||
|
|
f0c8788a52 | ||
|
|
a3cbb01b27 | ||
|
|
d630b4ae8a | ||
|
|
a2c5b2a567 | ||
|
|
18295f4402 | ||
|
|
443fd604cc | ||
|
|
9e022ff7cf | ||
|
|
aba927054f |
@@ -1,8 +1,2 @@
|
||||
# This file is _not_ included in the tarballs for now
|
||||
# Binary builds on Linux packaging infrastructure need to overwrite it to make `cargo vendor` work
|
||||
# Releases and development builds made using OBS/Launchpad will _not_ reflect the contents of this
|
||||
# file
|
||||
|
||||
[resolver]
|
||||
# Make cargo 1.84+ respect MSRV (rust-version in Cargo.toml)
|
||||
incompatible-rust-versions = "fallback"
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
|
||||
@@ -29,6 +29,7 @@ freebsd_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-15-0-release-amd64-ufs # updatecli.d/cirrus-freebsd.yml
|
||||
tests_script:
|
||||
- pkg update
|
||||
- pkg install -y cmake-core devel/pcre2 devel/ninja gettext git-lite lang/rust misc/py-pexpect
|
||||
# libclang.so is a required build dependency for rust-c++ ffi bridge
|
||||
- pkg install -y llvm
|
||||
|
||||
@@ -9,7 +9,7 @@ trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
|
||||
[{Makefile,*.in}]
|
||||
[{Makefile,{BSD,GNU}makefile}]
|
||||
indent_style = tab
|
||||
|
||||
[*.{md,rst}]
|
||||
@@ -21,7 +21,7 @@ indent_size = 4
|
||||
[build_tools/release.sh]
|
||||
max_line_length = 72
|
||||
|
||||
[Dockerfile]
|
||||
[{Dockerfile,Vagrantfile}]
|
||||
indent_size = 2
|
||||
|
||||
[share/{completions,functions}/**.fish]
|
||||
@@ -29,3 +29,6 @@ max_line_length = unset
|
||||
|
||||
[{COMMIT_EDITMSG,git-revise-todo,*.jjdescription}]
|
||||
max_line_length = 72
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -18,9 +18,6 @@
|
||||
/.github/* export-ignore
|
||||
/.builds export-ignore
|
||||
/.builds/* export-ignore
|
||||
# to make cargo vendor work correctly
|
||||
/.cargo/ export-ignore
|
||||
/.cargo/config.toml export-ignore
|
||||
|
||||
# for linguist, which drives GitHub's language statistics
|
||||
alpine.js linguist-vendored
|
||||
|
||||
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,11 +1,8 @@
|
||||
## Description
|
||||
|
||||
Talk about your changes here.
|
||||
|
||||
Fixes issue #
|
||||
|
||||
## TODOs:
|
||||
<!-- Just check off what what we know been done so far. We can help you with this stuff. -->
|
||||
<!-- Check off what what has been done so far. -->
|
||||
- [ ] If addressing an issue, a commit message mentions `Fixes issue #<issue-number>`
|
||||
- [ ] Changes to fish usage are reflected in user documentation/manpages.
|
||||
- [ ] Tests have been added for regressions fixed
|
||||
- [ ] User-visible changes noted in CHANGELOG.rst <!-- Don't document changes for completions inside CHANGELOG.rst, there are lot of such edits -->
|
||||
- [ ] User-visible changes noted in CHANGELOG.rst <!-- Usually skipped for changes to completions -->
|
||||
|
||||
2
.github/actions/rust-toolchain/action.yml
vendored
2
.github/actions/rust-toolchain/action.yml
vendored
@@ -25,7 +25,7 @@ runs:
|
||||
set -x
|
||||
toolchain=$(
|
||||
case "$toolchain_channel" in
|
||||
(stable) echo 1.92 ;; # updatecli.d/rust.yml
|
||||
(stable) echo 1.93 ;; # updatecli.d/rust.yml
|
||||
(msrv) echo 1.85 ;; # updatecli.d/rust.yml
|
||||
(*)
|
||||
printf >&2 "error: unsupported toolchain channel %s" "$toolchain_channel"
|
||||
|
||||
2
.github/workflows/autolabel_prs.yml
vendored
2
.github/workflows/autolabel_prs.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- name: Set label and milestone
|
||||
id: set-label-milestone
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8, build_tools/update-dependencies.sh
|
||||
with:
|
||||
script: |
|
||||
const completionsLabel = 'completions';
|
||||
|
||||
8
.github/workflows/build_docker_images.yml
vendored
8
.github/workflows/build_docker_images.yml
vendored
@@ -37,10 +37,10 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
-
|
||||
name: Login to Container registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -48,14 +48,14 @@ jobs:
|
||||
-
|
||||
name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.NAMESPACE }}/${{ matrix.target }}
|
||||
flavor: |
|
||||
latest=true
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
context: docker/context
|
||||
push: true
|
||||
|
||||
6
.github/workflows/lint-dependencies.yml
vendored
6
.github/workflows/lint-dependencies.yml
vendored
@@ -16,9 +16,9 @@ jobs:
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: EmbarkStudios/cargo-deny-action@44db170f6a7d12a6e90340e9e0fca1f650d34b14 # v2.0.15, build_tools/update-dependencies.sh
|
||||
with:
|
||||
command: check licenses
|
||||
arguments: --all-features --locked --exclude-dev
|
||||
rust-version: 1.92 # updatecli.d/rust.yml
|
||||
rust-version: 1.93 # updatecli.d/rust.yml
|
||||
|
||||
10
.github/workflows/lint.yml
vendored
10
.github/workflows/lint.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
@@ -35,11 +35,13 @@ jobs:
|
||||
- rust_version: "msrv"
|
||||
features: ""
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain
|
||||
with:
|
||||
toolchain_channel: ${{ matrix.rust_version }}
|
||||
components: clippy
|
||||
- name: Update package database
|
||||
run: sudo apt-get update
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install gettext
|
||||
@@ -49,8 +51,10 @@ jobs:
|
||||
rustdoc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain@stable
|
||||
- name: Update package database
|
||||
run: sudo apt-get update
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install gettext
|
||||
|
||||
2
.github/workflows/lockthreads.yml
vendored
2
.github/workflows/lockthreads.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
pull-requests: write # for dessant/lock-threads to lock PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
- uses: dessant/lock-threads@f5f995c727ac99a91dec92781a8e34e7c839a65e # v6.0.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '365'
|
||||
|
||||
22
.github/workflows/release.yml
vendored
22
.github/workflows/release.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
name: Pre-release checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
@@ -36,10 +36,12 @@ jobs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
tarball-name: ${{ steps.version.outputs.tarball-name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Update package database
|
||||
run: sudo apt-get update
|
||||
- name: Install dependencies
|
||||
run: sudo apt install cmake gettext ninja-build python3-pip
|
||||
- uses: ./.github/actions/install-sphinx
|
||||
@@ -61,7 +63,7 @@ jobs:
|
||||
sed -n 2p "$relnotes" | grep -q '^$'
|
||||
sed -i 1,2d "$relnotes"
|
||||
- name: Upload tarball artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
name: source-tarball
|
||||
path: |
|
||||
@@ -74,7 +76,7 @@ jobs:
|
||||
name: Build single-file fish for Linux
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
@@ -82,6 +84,8 @@ jobs:
|
||||
uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl
|
||||
- name: Update package database
|
||||
run: sudo apt-get update
|
||||
- name: Install dependencies
|
||||
run: sudo apt install crossbuild-essential-arm64 gettext musl-tools
|
||||
- uses: ./.github/actions/install-sphinx
|
||||
@@ -100,7 +104,7 @@ jobs:
|
||||
tar -cazf fish-$(git describe)-linux-$arch.tar.xz \
|
||||
-C target/$arch-unknown-linux-musl/release fish
|
||||
done
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
name: Static builds for Linux
|
||||
path: fish-${{ inputs.version }}-linux-*.tar.xz
|
||||
@@ -114,19 +118,19 @@ jobs:
|
||||
name: Create release draft
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: /tmp/artifacts
|
||||
- name: List artifacts
|
||||
run: find /tmp/artifacts -type f
|
||||
- name: Create draft release
|
||||
uses: softprops/action-gh-release@v2
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
tag_name: ${{ inputs.version }}
|
||||
name: fish ${{ inputs.version }}
|
||||
@@ -142,7 +146,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
environment: macos-codesign
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
|
||||
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
- name: Install deps
|
||||
uses: ./.github/actions/install-dependencies
|
||||
@@ -44,18 +44,19 @@ jobs:
|
||||
ubuntu-32bit-static-pcre2:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
with:
|
||||
targets: "i586-unknown-linux-gnu"
|
||||
- name: Update package database
|
||||
run: sudo apt-get update
|
||||
- name: Install deps
|
||||
uses: ./.github/actions/install-dependencies
|
||||
with:
|
||||
include_pcre: false
|
||||
include_sphinx: false
|
||||
- name: Install g++-multilib
|
||||
run: |
|
||||
sudo apt install g++-multilib
|
||||
run: sudo apt install g++-multilib
|
||||
- name: cmake
|
||||
env:
|
||||
CFLAGS: "-m32"
|
||||
@@ -80,13 +81,15 @@ jobs:
|
||||
RUSTFLAGS: "-Zsanitizer=address"
|
||||
# RUSTFLAGS: "-Zsanitizer=memory -Zsanitizer-memory-track-origins"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
# All -Z options require running nightly
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
# ASAN uses `cargo build -Zbuild-std` which requires the rust-src component
|
||||
# this is comma-separated
|
||||
components: rust-src
|
||||
- name: Update package database
|
||||
run: sudo apt-get update
|
||||
- name: Install deps
|
||||
uses: ./.github/actions/install-dependencies
|
||||
with:
|
||||
@@ -128,7 +131,7 @@ jobs:
|
||||
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
- name: Install deps
|
||||
run: |
|
||||
@@ -155,8 +158,8 @@ jobs:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: msys2/setup-msys2@v2
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: msys2/setup-msys2@4f806de0a5a7294ffabaff804b38a9b435a73bda # v2.30.0, build_tools/update-dependencies.sh
|
||||
with:
|
||||
update: true
|
||||
msystem: MSYS
|
||||
|
||||
3
AGENTS.md
Normal file
3
AGENTS.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Coding style
|
||||
|
||||
- Use comments sparingly. Don't explain what the code is doing, rather explain why.
|
||||
103
CHANGELOG.rst
103
CHANGELOG.rst
@@ -1,9 +1,104 @@
|
||||
fish 4.5.0 (released February 17, 2026)
|
||||
=======================================
|
||||
|
||||
This is mostly a patch release for Vi mode regressions in 4.4.0 but other minor behavior changes are included as well.
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
- :kbd:`ctrl-l` no longer cancels history search (:issue:`12436`).
|
||||
- History search cursor positioning now works correctly with characters of arbitrary width.
|
||||
|
||||
Deprecations and removed features
|
||||
---------------------------------
|
||||
- fish no longer reads the terminfo database to alter behaviour based on the :envvar:`TERM` environment variable, and does not depend on ncurses or terminfo. The ``ignore-terminfo`` feature flag, introduced and enabled by default in fish 4.1, is now permanently enabled. fish may no longer work correctly on Data General Dasher D220 and Wyse WY-350 terminals, but should continue to work on all known terminal emulators released in the 21st century.
|
||||
|
||||
Regression fixes:
|
||||
-----------------
|
||||
- (from 4.4.0) Vi mode ``d,f`` key binding did not work (:issue:`12417`).
|
||||
- (from 4.4.0) Vi mode ``c,w`` key binding wrongly deleted trailing spaces (:issue:`12443`).
|
||||
- (from 4.4.0) Vi mode crash on ``c,i,w`` after accepting autosuggestion (:issue:`12430`).
|
||||
- (from 4.4.0) ``fish_vi_key_bindings`` called with a mode argument produced an error (:issue:`12413`).
|
||||
- (from 4.0.0) Build on Illumos (:issue:`12410`).
|
||||
|
||||
fish 4.4.0 (released February 03, 2026)
|
||||
=======================================
|
||||
|
||||
Deprecations and removed features
|
||||
---------------------------------
|
||||
- The default fossil prompt has been disabled (:issue:`12342`).
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
- The ``bind`` builtin lists mappings from all modes if ``--mode`` is not provided (:issue:`12214`).
|
||||
- Line-wise autosuggestions that don't start a command are no longer shown (739b82c34db, 58e7a50de8a).
|
||||
- Builtin ``history`` now assumes that :envvar:`PAGER` supports ANSI color sequences.
|
||||
- fish now clears the terminal's ``FLUSHO`` flag when acquiring control of the terminal, to fix an issue caused by pressing :kbd:`ctrl-o` on macOS (:issue:`12304`).
|
||||
|
||||
New or improved bindings
|
||||
------------------------
|
||||
- Vi mode word movements (``w``, ``W``, ``e``, and ``E``) are now largely in line with Vim. The only exception is that underscores are treated as word separators (:issue:`12269`).
|
||||
- New special input functions to support these movements: ``forward-word-vi``, ``kill-word-vi``, ``forward-bigword-vi``, ``kill-bigword-vi``, ``forward-word-end``, ``backward-word-end``, ``forward-bigword-end``, ``backward-bigword-end``, ``kill-a-word``, ``kill-inner-word``, ``kill-a-bigword``, and ``kill-inner-bigword``.
|
||||
- Vi mode key bindings now support counts for movement and deletion commands (e.g. `d3w` or `3l`), via a new operator mode (:issue:`2192`).
|
||||
- New ``catpuccin-*`` color themes.
|
||||
|
||||
Improved terminal support
|
||||
-------------------------
|
||||
- ``set_color`` learned the strikethrough (``--strikethrough`` or ``-s``) modifier.
|
||||
|
||||
For distributors and developers
|
||||
-------------------------------
|
||||
- The CMake option ``WITH_GETTEXT`` has been renamed to ``WITH_MESSAGE_LOCALIZATION``, to reflect that it toggles localization independently of the backend used in the implementation.
|
||||
- New ``cargo xtask`` commands can replace some CMake workflows.
|
||||
|
||||
Regression fixes:
|
||||
-----------------
|
||||
- (from 4.1.0) Crash when autosuggesting Unicode characters with nontrivial lowercase mapping (:issue:`12326`, 78f4541116e).
|
||||
- (from 4.3.0) Glitch on ``read --prompt-str ""`` (:issue:`12296`).
|
||||
|
||||
fish 4.3.3 (released January 07, 2026)
|
||||
======================================
|
||||
|
||||
This release fixes the following problems identified in fish 4.3.0:
|
||||
|
||||
- Selecting a completion could insert only part of the token (:issue:`12249`).
|
||||
- Glitch with soft-wrapped autosuggestions and :doc:`fish_right_prompt <cmds/fish_right_prompt>` (:issue:`12255`).
|
||||
- Spurious echo in tmux when typing a command really fast (:issue:`12261`).
|
||||
- ``tomorrow`` theme always using the light variant (:issue:`12266`).
|
||||
- ``fish_config theme choose`` sometimes not shadowing themes set by e.g. webconfig (:issue:`12278`).
|
||||
- The sample prompts and themes are correctly installed (:issue:`12241`).
|
||||
- Last line of command output could be hidden when missing newline (:issue:`12246`).
|
||||
|
||||
Other improvements include:
|
||||
|
||||
- The ``abbr``, ``bind``, ``complete``, ``functions``, ``history`` and ``type`` commands now support a ``--color`` option to control syntax highlighting in their output. Valid values are ``auto`` (default), ``always``, or ``never``.
|
||||
- Existing file paths in redirection targets such as ``> file.txt`` are now highlighted using :envvar:`fish_color_valid_path`, indicating that ``file.txt`` will be clobbered (:issue:`12260`).
|
||||
|
||||
fish 4.3.2 (released December 30, 2025)
|
||||
=======================================
|
||||
|
||||
This release fixes the following problems identified in 4.3.0:
|
||||
|
||||
- Pre-built macOS packages failed to start due to a ``Malformed Mach-O file`` error (:issue:`12224`).
|
||||
- ``extra_functionsdir`` (usually ``vendor_functions.d``) and friends were not used (:issue:`12226`).
|
||||
- Sample config file ``~/.config/fish/config.fish/`` and config directories ``~/.config/fish/conf.d/``, ``~/.config/fish/completions/`` and ``~/.config/fish/functions/`` were recreated on every startup instead of only the first time fish runs on a system (:issue:`12230`).
|
||||
- Spurious echo of ``^[[I`` in some scenarios (:issue:`12232`).
|
||||
- Infinite prompt redraw loop on some prompts (:issue:`12233`).
|
||||
- The removal of pre-built HTML docs from tarballs revealed that cross compilation is broken because we use ``${CMAKE_BINARY_DIR}/fish_indent`` for building HTML docs.
|
||||
As a workaround, the new CMake build option ``FISH_INDENT_FOR_BUILDING_DOCS`` can be set to the path of a runnable ``fish_indent`` binary.
|
||||
|
||||
fish 4.3.1 (released December 28, 2025)
|
||||
=======================================
|
||||
|
||||
This release fixes the following problem identified in 4.3.0:
|
||||
|
||||
- Possible crash after expanding an abbreviation (:issue:`12223`).
|
||||
|
||||
fish 4.3.0 (released December 28, 2025)
|
||||
=======================================
|
||||
|
||||
Deprecations and removed features
|
||||
---------------------------------
|
||||
- fish no longer sets :ref:`universal variables <variables-universal>` by default, making the configuration easier to understand.
|
||||
- fish no longer sets user-facing :ref:`universal variables <variables-universal>` by default, making the configuration easier to understand.
|
||||
Specifically, the ``fish_color_*``, ``fish_pager_color_*`` and ``fish_key_bindings`` variables are now set in the global scope by default.
|
||||
After upgrading to 4.3.0, fish will (once and never again) migrate these universals to globals set at startup in the
|
||||
``~/.config/fish/conf.d/fish_frozen_theme.fish`` and
|
||||
@@ -30,7 +125,7 @@ Interactive improvements
|
||||
- Completion accuracy was improved for file paths containing ``=`` or ``:`` (:issue:`5363`).
|
||||
- Prefix-matching completions are now shown even if they don't match the case typed by the user (:issue:`7944`).
|
||||
- On Cygwin/MSYS, command name completion will favor the non-exe name (``foo``) unless the user started typing the extension.
|
||||
- When using the exe name (``foo.exe``), fish will use to the description and completions for ``foo`` if there are none for ``foo.exe``.
|
||||
- When using the exe name (``foo.exe``), fish will use the description and completions for ``foo`` if there are none for ``foo.exe``.
|
||||
- Autosuggestions now also show soft-wrapped portions (:issue:`12045`).
|
||||
|
||||
New or improved bindings
|
||||
@@ -45,8 +140,6 @@ Improved terminal support
|
||||
- The working directory is now reported on every fresh prompt (via OSC 7), fixing scenarios where a child process (like ``ssh``) left behind a stale working directory (:issue:`12191`).
|
||||
- OSC 133 prompt markers now also mark the prompt end, which improves shell integration with terminals like iTerm2 (:issue:`11837`).
|
||||
- Operating-system-specific key bindings are now decided based on the :ref:`terminal's host OS <status-terminal-os>`.
|
||||
- Focus reporting is enabled unconditionally, not just inside tmux.
|
||||
To use it, define functions that handle the ``fish_focus_in`` or ``fish_focus_out`` :ref:`events <event>`.
|
||||
- New :ref:`feature flag <featureflags>` ``omit-term-workarounds`` can be turned on to prevent fish from trying to work around some incompatible terminals.
|
||||
|
||||
For distributors and developers
|
||||
@@ -176,8 +269,6 @@ This release fixes the following regressions identified in 4.1.0:
|
||||
fish 4.1.0 (released September 27, 2025)
|
||||
========================================
|
||||
|
||||
.. ignore for 4.1: 10929 10940 10948 10955 10965 10975 10989 10990 10998 11028 11052 11055 11069 11071 11079 11092 11098 11104 11106 11110 11140 11146 11148 11150 11214 11218 11259 11288 11299 11328 11350 11373 11395 11417 11419
|
||||
|
||||
Notable improvements and fixes
|
||||
------------------------------
|
||||
- Compound commands (``begin; echo 1; echo 2; end``) can now be written using braces (``{ echo1; echo 2 }``), like in other shells.
|
||||
|
||||
@@ -24,12 +24,12 @@ include(cmake/Rust.cmake)
|
||||
# Work around issue where archive-built libs go in the wrong place.
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
|
||||
# Set up the machinery around FISH-BUILD-VERSION-FILE
|
||||
# This defines the FBVF variable.
|
||||
include(Version)
|
||||
find_program(SPHINX_EXECUTABLE NAMES sphinx-build
|
||||
HINTS
|
||||
$ENV{SPHINX_DIR}
|
||||
PATH_SUFFIXES bin
|
||||
DOC "Sphinx documentation generator")
|
||||
|
||||
# Set up the docs.
|
||||
include(cmake/Docs.cmake)
|
||||
|
||||
# Tell Cargo where our build directory is so it can find Cargo.toml.
|
||||
set(VARS_FOR_CARGO
|
||||
@@ -91,6 +91,9 @@ create_link(fish_indent)
|
||||
# Define fish_key_reader.
|
||||
create_link(fish_key_reader)
|
||||
|
||||
# Set up the docs.
|
||||
include(cmake/Docs.cmake)
|
||||
|
||||
# Set up tests.
|
||||
include(cmake/Tests.cmake)
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ Guidelines
|
||||
In short:
|
||||
|
||||
- Be conservative in what you need (keep to the agreed minimum supported Rust version, limit new dependencies)
|
||||
- Use automated tools to help you (``build_tools/check.sh``)
|
||||
- Use automated tools to help you (``cargo xtask check``)
|
||||
|
||||
Commit History
|
||||
==============
|
||||
@@ -105,21 +105,24 @@ Contributing documentation
|
||||
==========================
|
||||
|
||||
The documentation is stored in ``doc_src/``, and written in ReStructured Text and built with Sphinx.
|
||||
The builtins and various functions shipped with fish are documented in ``doc_src/cmds/``.
|
||||
|
||||
To build it locally, run either::
|
||||
To build an HTML version of the docs locally, run::
|
||||
|
||||
sphinx-build -j auto -b html doc_src/ /tmp/fish-doc/
|
||||
cargo xtask html-docs
|
||||
|
||||
which will output HTML docs to /tmp/fish-doc.
|
||||
You can open it in a browser and see that it looks okay.
|
||||
|
||||
Alternatively, you can use::
|
||||
will output to ``target/fish-docs/html`` or, if you use CMake::
|
||||
|
||||
cmake --build build -t sphinx-docs
|
||||
|
||||
which outputs to build/user_doc/html/.
|
||||
will output to ``build/cargo/fish-docs/html/``. You can also run ``sphinx-build`` directly, which allows choosing the output directory::
|
||||
|
||||
sphinx-build -j auto -b html doc_src/ /tmp/fish-doc/
|
||||
|
||||
will output HTML docs to ``/tmp/fish-doc``.
|
||||
|
||||
After building them, you can open the HTML docs in a browser and see that it looks okay.
|
||||
|
||||
The builtins and various functions shipped with fish are documented in doc_src/cmds/.
|
||||
|
||||
Code Style
|
||||
==========
|
||||
@@ -245,7 +248,7 @@ In this example we're in the root of the workspace and have run ``cargo build``
|
||||
|
||||
To run all tests and linters, use::
|
||||
|
||||
build_tools/check.sh
|
||||
cargo xtask check
|
||||
|
||||
Contributing Translations
|
||||
=========================
|
||||
|
||||
475
Cargo.lock
generated
475
Cargo.lock
generated
@@ -4,9 +4,9 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -17,6 +17,62 @@ version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert_matches"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@@ -40,9 +96,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
@@ -50,9 +106,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.41"
|
||||
version = "1.2.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
|
||||
checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -72,6 +128,52 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
@@ -83,9 +185,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
@@ -122,6 +224,12 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -146,20 +254,22 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "4.3.0"
|
||||
version = "4.5.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"bitflags",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"errno",
|
||||
"fish-build-helper",
|
||||
"fish-build-man-pages",
|
||||
"fish-color",
|
||||
"fish-common",
|
||||
"fish-fallback",
|
||||
"fish-gettext",
|
||||
@@ -167,8 +277,12 @@ dependencies = [
|
||||
"fish-gettext-mo-file-parser",
|
||||
"fish-printf",
|
||||
"fish-tempfile",
|
||||
"fish-wchar",
|
||||
"fish-util",
|
||||
"fish-wcstringutil",
|
||||
"fish-wgetopt",
|
||||
"fish-widecharwidth",
|
||||
"fish-widestring",
|
||||
"itertools",
|
||||
"libc",
|
||||
"lru",
|
||||
"macro_rules_attribute",
|
||||
@@ -176,15 +290,14 @@ dependencies = [
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"pcre2",
|
||||
"phf_codegen 0.12.1",
|
||||
"phf_codegen",
|
||||
"portable-atomic",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
"rsconf",
|
||||
"rust-embed",
|
||||
"serial_test",
|
||||
"terminfo",
|
||||
"unicode-width",
|
||||
"unix_path",
|
||||
"widestring",
|
||||
"xterm-color",
|
||||
]
|
||||
|
||||
@@ -203,13 +316,22 @@ dependencies = [
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-color"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-common",
|
||||
"fish-widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-common"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fish-widestring",
|
||||
"libc",
|
||||
"nix",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -218,10 +340,9 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-build-helper",
|
||||
"fish-common",
|
||||
"fish-wchar",
|
||||
"fish-widecharwidth",
|
||||
"fish-widestring",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"rsconf",
|
||||
"widestring",
|
||||
]
|
||||
@@ -231,8 +352,7 @@ name = "fish-gettext"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-gettext-maps",
|
||||
"once_cell",
|
||||
"phf 0.12.1",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -250,8 +370,8 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-build-helper",
|
||||
"fish-gettext-mo-file-parser",
|
||||
"phf 0.12.1",
|
||||
"phf_codegen 0.12.1",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
@@ -263,6 +383,7 @@ version = "0.0.0"
|
||||
name = "fish-printf"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"libc",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
@@ -274,15 +395,35 @@ name = "fish-tempfile"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-wchar"
|
||||
name = "fish-util"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-widestring",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-wcstringutil"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-build-helper",
|
||||
"fish-common",
|
||||
"widestring",
|
||||
"fish-fallback",
|
||||
"fish-widestring",
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-wgetopt"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"fish-wcstringutil",
|
||||
"fish-widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -290,22 +431,23 @@ name = "fish-widecharwidth"
|
||||
version = "0.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
name = "fish-widestring"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
@@ -313,9 +455,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -336,9 +478,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.16"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
||||
checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
@@ -349,15 +491,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
@@ -370,15 +533,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.177"
|
||||
version = "0.2.180"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -395,15 +558,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.13.0"
|
||||
version = "0.16.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465"
|
||||
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
@@ -430,17 +593,11 @@ version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
||||
checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
@@ -448,16 +605,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -473,6 +620,12 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
@@ -530,76 +683,38 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
|
||||
dependencies = [
|
||||
"phf_shared 0.12.1",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.3",
|
||||
"phf_shared 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61"
|
||||
dependencies = [
|
||||
"phf_generator 0.12.1",
|
||||
"phf_shared 0.12.1",
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b"
|
||||
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"phf_shared 0.12.1",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981"
|
||||
checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
@@ -612,9 +727,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
@@ -627,18 +742,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -649,15 +764,6 @@ version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
@@ -665,7 +771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.3",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -675,20 +781,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
@@ -708,7 +808,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"getrandom 0.2.17",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -732,18 +832,18 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rsconf"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd2af859f1af0401e7fc7577739c87b0d239d8a5da400d717183bca92336bcdc"
|
||||
checksum = "06cbd984e96cc891aa018958ac3d09986c0ea7635eedfff670b99a90970f159f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.9.0"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca"
|
||||
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
@@ -752,9 +852,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.9.0"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2"
|
||||
checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -766,9 +866,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.9.0"
|
||||
version = "8.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475"
|
||||
checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"sha2",
|
||||
@@ -836,9 +936,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "3.2.0"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
|
||||
checksum = "0d0b343e184fc3b7bb44dff0705fffcf4b3756ba6aff420dddd8b24ca145e555"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
@@ -848,9 +948,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "3.2.0"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
|
||||
checksum = "6f50427f258fb77356e4cd4aa0e87e2bd2c66dbcee41dc405282cae2bfc26c83"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -885,9 +985,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@@ -896,42 +996,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.107"
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"nom",
|
||||
"phf 0.11.3",
|
||||
"phf_codegen 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.17"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -946,9 +1040,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.20"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
@@ -977,6 +1071,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ace0b4755d0a2959962769239d56267f8a024fef2d9b32666b3dcd0946b0906"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -1044,26 +1144,35 @@ version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fish-build-helper",
|
||||
"fish-tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xterm-color"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4de5f056fb9dc8b7908754867544e26145767187aaac5a98495e88ad7cb8a80f"
|
||||
checksum = "7008a9d8ba97a7e47d9b2df63fcdb8dade303010c5a7cd5bf2469d4da6eba673"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
version = "0.8.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.27"
|
||||
version = "0.8.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
||||
checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
81
Cargo.toml
81
Cargo.toml
@@ -11,12 +11,15 @@ repository = "https://github.com/fish-shell/fish-shell"
|
||||
license = "GPL-2.0-only AND LGPL-2.0-or-later AND MIT AND PSF-2.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
bitflags = "2.5.0"
|
||||
cc = "1.0.94"
|
||||
cfg-if = "1.0.3"
|
||||
clap = { version = "4.5.54", features = ["derive"] }
|
||||
errno = "0.3.0"
|
||||
fish-build-helper = { path = "crates/build-helper" }
|
||||
fish-build-man-pages = { path = "crates/build-man-pages" }
|
||||
fish-color = { path = "crates/color" }
|
||||
fish-common = { path = "crates/common" }
|
||||
fish-fallback = { path = "crates/fallback" }
|
||||
fish-gettext = { path = "crates/gettext" }
|
||||
@@ -25,27 +28,35 @@ fish-gettext-maps = { path = "crates/gettext-maps" }
|
||||
fish-gettext-mo-file-parser = { path = "crates/gettext-mo-file-parser" }
|
||||
fish-printf = { path = "crates/printf", features = ["widestring"] }
|
||||
fish-tempfile = { path = "crates/tempfile" }
|
||||
fish-wchar = { path = "crates/wchar" }
|
||||
fish-util = { path = "crates/util" }
|
||||
fish-wcstringutil = { path = "crates/wcstringutil" }
|
||||
fish-widecharwidth = { path = "crates/widecharwidth" }
|
||||
fish-widestring = { path = "crates/widestring" }
|
||||
fish-wgetopt = { path = "crates/wgetopt" }
|
||||
itertools = "0.14.0"
|
||||
libc = "0.2.177"
|
||||
# lru pulls in hashbrown by default, which uses a faster (though less DoS resistant) hashing algo.
|
||||
# disabling default features uses the stdlib instead, but it doubles the time to rewrite the history
|
||||
# files as of 22 April 2024.
|
||||
lru = "0.13.0"
|
||||
nix = { version = "0.30.1", default-features = false, features = [
|
||||
lru = "0.16.2"
|
||||
nix = { version = "0.31.1", default-features = false, features = [
|
||||
"event",
|
||||
"inotify",
|
||||
"resource",
|
||||
"fs",
|
||||
"inotify",
|
||||
"hostname",
|
||||
"resource",
|
||||
"process",
|
||||
"signal",
|
||||
"term",
|
||||
"user",
|
||||
] }
|
||||
num-traits = "0.2.19"
|
||||
once_cell = "1.19.0"
|
||||
pcre2 = { git = "https://github.com/fish-shell/rust-pcre2", tag = "0.2.9-utf32", default-features = false, features = [
|
||||
"utf32",
|
||||
] }
|
||||
phf = { version = "0.12", default-features = false }
|
||||
phf_codegen = { version = "0.12" }
|
||||
phf = { version = "0.13", default-features = false }
|
||||
phf_codegen = "0.13"
|
||||
portable-atomic = { version = "1", default-features = false, features = [
|
||||
"fallback",
|
||||
] }
|
||||
@@ -54,15 +65,13 @@ rand = { version = "0.9.2", default-features = false, features = [
|
||||
"small_rng",
|
||||
"thread_rng",
|
||||
] }
|
||||
rsconf = "0.2.2"
|
||||
rust-embed = { version = "8.9.0", features = [
|
||||
rsconf = "0.3.0"
|
||||
rust-embed = { version = "8.11.0", features = [
|
||||
"deterministic-timestamps",
|
||||
"include-exclude",
|
||||
"interpolate-folder-path",
|
||||
] }
|
||||
serial_test = { version = "3", default-features = false }
|
||||
# We need 0.9.0 specifically for some crash fixes.
|
||||
terminfo = "0.9.0"
|
||||
widestring = "1.2.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
unicode-width = "0.2.0"
|
||||
@@ -79,7 +88,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.3.0"
|
||||
version = "4.5.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
default-run = "fish"
|
||||
@@ -88,19 +97,25 @@ homepage = "https://fishshell.com"
|
||||
readme = "README.rst"
|
||||
|
||||
[dependencies]
|
||||
assert_matches.workspace = true
|
||||
bitflags.workspace = true
|
||||
cfg-if.workspace = true
|
||||
errno.workspace = true
|
||||
fish-build-helper.workspace = true
|
||||
fish-build-man-pages = { workspace = true, optional = true }
|
||||
fish-color.workspace = true
|
||||
fish-common.workspace = true
|
||||
fish-fallback.workspace = true
|
||||
fish-gettext = { workspace = true, optional = true }
|
||||
fish-gettext-extraction = { workspace = true, optional = true }
|
||||
fish-printf.workspace = true
|
||||
fish-tempfile.workspace = true
|
||||
fish-wchar.workspace = true
|
||||
fish-util.workspace = true
|
||||
fish-wcstringutil.workspace = true
|
||||
fish-wgetopt.workspace = true
|
||||
fish-widecharwidth.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
itertools.workspace = true
|
||||
libc.workspace = true
|
||||
lru.workspace = true
|
||||
macro_rules_attribute = "0.2.2"
|
||||
@@ -109,9 +124,8 @@ num-traits.workspace = true
|
||||
once_cell.workspace = true
|
||||
pcre2.workspace = true
|
||||
rand.workspace = true
|
||||
terminfo.workspace = true
|
||||
unicode-width.workspace = true
|
||||
xterm-color.workspace = true
|
||||
widestring.workspace = true
|
||||
|
||||
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
|
||||
portable-atomic.workspace = true
|
||||
@@ -168,12 +182,11 @@ embed-manpages = ["dep:fish-build-man-pages"]
|
||||
localize-messages = ["dep:fish-gettext"]
|
||||
# This feature is used to enable extracting messages from the source code for localization.
|
||||
# It only needs to be enabled if updating these messages (and the corresponding PO files) is
|
||||
# desired. This happens when running tests via `build_tools/check.sh` and when calling
|
||||
# desired. This happens when running tests via `cargo xtask check` and when calling
|
||||
# `build_tools/update_translations.fish`, so there should not be a need to enable it manually.
|
||||
gettext-extract = ["dep:fish-gettext-extraction"]
|
||||
|
||||
# The following features are auto-detected by the build-script and should not be enabled manually.
|
||||
asan = []
|
||||
tsan = []
|
||||
|
||||
[workspace.lints]
|
||||
@@ -182,19 +195,35 @@ rust.non_upper_case_globals = "allow"
|
||||
rust.unknown_lints = "allow"
|
||||
rust.unstable_name_collisions = "allow"
|
||||
rustdoc.private_intra_doc_links = "allow"
|
||||
clippy.len_without_is_empty = "allow" # we're not a library crate
|
||||
clippy.let_and_return = "allow"
|
||||
clippy.manual_range_contains = "allow"
|
||||
clippy.map_unwrap_or = "warn"
|
||||
clippy.needless_lifetimes = "allow"
|
||||
clippy.new_without_default = "allow"
|
||||
clippy.option_map_unit_fn = "allow"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
assigning_clones = "warn"
|
||||
cloned_instead_of_copied = "warn"
|
||||
explicit_into_iter_loop = "warn"
|
||||
format_push_string = "warn"
|
||||
implicit_clone = "warn"
|
||||
len_without_is_empty = "allow" # we're not a library crate
|
||||
let_and_return = "allow"
|
||||
manual_assert = "warn"
|
||||
manual_range_contains = "allow"
|
||||
map_unwrap_or = "warn"
|
||||
mut_mut = "warn"
|
||||
needless_lifetimes = "allow"
|
||||
new_without_default = "allow"
|
||||
option_map_unit_fn = "allow"
|
||||
ptr_offset_by_literal = "warn"
|
||||
ref_option = "warn"
|
||||
semicolon_if_nothing_returned = "warn"
|
||||
stable_sort_primitive = "warn"
|
||||
str_to_string = "warn"
|
||||
unnecessary_semicolon = "warn"
|
||||
unused_trait_names = "warn"
|
||||
|
||||
# We do not want to use the e?print(ln)?! macros.
|
||||
# These lints flag their use.
|
||||
# In the future, they might change to flag other methods of printing.
|
||||
clippy.print_stdout = "deny"
|
||||
clippy.print_stderr = "deny"
|
||||
print_stdout = "deny"
|
||||
print_stderr = "deny"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
18
Dockerfile
18
Dockerfile
@@ -1,18 +0,0 @@
|
||||
FROM centos:latest
|
||||
|
||||
# Build dependency
|
||||
RUN yum update -y &&\
|
||||
yum install -y epel-release &&\
|
||||
yum install -y clang cmake3 gcc-c++ make &&\
|
||||
yum clean all
|
||||
|
||||
# Test dependency
|
||||
RUN yum install -y expect vim-common
|
||||
|
||||
ADD . /src
|
||||
WORKDIR /src
|
||||
|
||||
# Build fish
|
||||
RUN cmake3 . &&\
|
||||
make &&\
|
||||
make install
|
||||
@@ -7,7 +7,7 @@
|
||||
CMAKE ?= cmake
|
||||
|
||||
GENERATOR ?= $(shell (which ninja > /dev/null 2> /dev/null && echo Ninja) || \
|
||||
echo 'Unix Makefiles')
|
||||
echo 'Unix Makefiles')
|
||||
prefix ?= /usr/local
|
||||
PREFIX ?= $(prefix)
|
||||
|
||||
@@ -34,7 +34,7 @@ all: .begin build/fish
|
||||
.PHONY: .begin
|
||||
.begin:
|
||||
@which $(CMAKE) > /dev/null 2> /dev/null || \
|
||||
(echo 'Please install CMake and then re-run the `make` command!' 1>&2 && false)
|
||||
(echo 'Please install CMake and then re-run the `make` command!' 1>&2 && false)
|
||||
|
||||
.PHONY: build/fish
|
||||
build/fish: build/$(BUILDFILE)
|
||||
|
||||
@@ -117,7 +117,7 @@ Dependencies
|
||||
|
||||
Compiling fish requires:
|
||||
|
||||
- Rust (version 1.85 or later)
|
||||
- Rust (version 1.85 or later), including cargo
|
||||
- CMake (version 3.15 or later)
|
||||
- a C compiler (for system feature detection and the test helper binary)
|
||||
- PCRE2 (headers and libraries) - optional, this will be downloaded if missing
|
||||
@@ -158,8 +158,13 @@ In addition to the normal CMake build options (like ``CMAKE_INSTALL_PREFIX``), f
|
||||
- Rust_CARGO=path - the path to cargo. If not set, cmake will check $PATH and ~/.cargo/bin
|
||||
- Rust_CARGO_TARGET=target - the target to pass to cargo. Set this for cross-compilation.
|
||||
- WITH_DOCS=ON|OFF - whether to build the documentation. By default, this is ON when Sphinx is installed.
|
||||
- FISH_INDENT_FOR_BUILDING_DOCS - useful for cross-compilation.
|
||||
Set this to the path to the ``fish_indent`` executable to use for building HTML docs.
|
||||
By default, ``${CMAKE_BINARY_DIR}/fish_indent`` will be used.
|
||||
If that's not runnable on the compile host,
|
||||
you can build a native one with ``cargo build --bin fish_indent`` and set this to ``$PWD/target/debug/fish_indent``.
|
||||
- FISH_USE_SYSTEM_PCRE2=ON|OFF - whether to use an installed pcre2. This is normally autodetected.
|
||||
- WITH_GETTEXT=ON|OFF - whether to include translations.
|
||||
- WITH_MESSAGE_LOCALIZATION=ON|OFF - whether to include translations.
|
||||
- extra_functionsdir, extra_completionsdir and extra_confdir - to compile in an additional directory to be searched for functions, completions and configuration snippets
|
||||
|
||||
Building fish with Cargo
|
||||
|
||||
107
build.rs
107
build.rs
@@ -3,13 +3,8 @@
|
||||
workspace_root,
|
||||
};
|
||||
use rsconf::Target;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn canonicalize<P: AsRef<Path>>(path: P) -> PathBuf {
|
||||
std::fs::canonicalize(path).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
setup_paths();
|
||||
|
||||
@@ -20,14 +15,14 @@ fn main() {
|
||||
"FISH_RESOLVED_BUILD_DIR",
|
||||
// If set by CMake, this might include symlinks. Since we want to compare this to the
|
||||
// dir fish is executed in we need to canonicalize it.
|
||||
canonicalize(fish_build_dir()).to_str().unwrap(),
|
||||
fish_build_dir().canonicalize().unwrap().to_str().unwrap(),
|
||||
);
|
||||
|
||||
// We need to canonicalize (i.e. realpath) the manifest dir because we want to be able to
|
||||
// compare it directly as a string at runtime.
|
||||
rsconf::set_env_value(
|
||||
"CARGO_MANIFEST_DIR",
|
||||
canonicalize(workspace_root()).to_str().unwrap(),
|
||||
workspace_root().canonicalize().unwrap().to_str().unwrap(),
|
||||
);
|
||||
|
||||
// Some build info
|
||||
@@ -35,13 +30,9 @@ fn main() {
|
||||
rsconf::set_env_value("BUILD_HOST_TRIPLE", &env_var("HOST").unwrap());
|
||||
rsconf::set_env_value("BUILD_PROFILE", &env_var("PROFILE").unwrap());
|
||||
|
||||
let version = &get_version(&env::current_dir().unwrap());
|
||||
// Per https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script,
|
||||
// the source directory is the current working directory of the build script
|
||||
rsconf::set_env_value("FISH_BUILD_VERSION", version);
|
||||
|
||||
// safety: single-threaded code.
|
||||
unsafe { std::env::set_var("FISH_BUILD_VERSION", version) };
|
||||
rsconf::set_env_value("FISH_BUILD_VERSION", &get_version());
|
||||
|
||||
fish_build_helper::rebuild_if_embedded_path_changed("share");
|
||||
|
||||
@@ -107,7 +98,7 @@ fn detect_cfgs(target: &mut Target) {
|
||||
target.r#if("WEXITSTATUS(0x007f) == 0x7f", &["sys/wait.h"])
|
||||
}),
|
||||
] {
|
||||
rsconf::declare_cfg(name, handler(target))
|
||||
rsconf::declare_cfg(name, handler(target));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,16 +159,14 @@ fn join_if_relative(parent_if_relative: &Path, path: String) -> PathBuf {
|
||||
}
|
||||
|
||||
let prefix = overridable_path("PREFIX", |env_prefix| {
|
||||
Some(PathBuf::from(
|
||||
env_prefix.unwrap_or("/usr/local".to_string()),
|
||||
))
|
||||
Some(PathBuf::from(env_prefix.unwrap_or("/usr/local".to_owned())))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
overridable_path("SYSCONFDIR", |env_sysconfdir| {
|
||||
Some(join_if_relative(
|
||||
&prefix,
|
||||
env_sysconfdir.unwrap_or("/etc/".to_string()),
|
||||
env_sysconfdir.unwrap_or("/etc/".to_owned()),
|
||||
))
|
||||
});
|
||||
|
||||
@@ -198,79 +187,15 @@ fn join_if_relative(parent_if_relative: &Path, path: String) -> PathBuf {
|
||||
});
|
||||
}
|
||||
|
||||
fn get_version(src_dir: &Path) -> String {
|
||||
use std::fs::read_to_string;
|
||||
fn get_version() -> String {
|
||||
use std::process::Command;
|
||||
|
||||
if let Some(var) = env_var("FISH_BUILD_VERSION") {
|
||||
return var;
|
||||
}
|
||||
|
||||
let path = src_dir.join("version");
|
||||
if let Ok(strver) = read_to_string(path) {
|
||||
return strver;
|
||||
}
|
||||
|
||||
let args = &["describe", "--always", "--dirty=-dirty"];
|
||||
if let Ok(output) = Command::new("git").args(args).output() {
|
||||
let rev = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
if !rev.is_empty() {
|
||||
// If it contains a ".", we have a proper version like "3.7",
|
||||
// or "23.2.1-1234-gfab1234"
|
||||
if rev.contains('.') {
|
||||
return rev;
|
||||
}
|
||||
// If it doesn't, we probably got *just* the commit SHA,
|
||||
// like "f1242abcdef".
|
||||
// So we prepend the crate version so it at least looks like
|
||||
// "3.8-gf1242abcdef"
|
||||
// This lacks the commit *distance*, but that can't be helped without
|
||||
// tags.
|
||||
let version = env!("CARGO_PKG_VERSION").to_owned();
|
||||
return version + "-g" + &rev;
|
||||
}
|
||||
}
|
||||
|
||||
// git did not tell us a SHA either because it isn't installed,
|
||||
// or because it refused (safe.directory applies to `git describe`!)
|
||||
// So we read the SHA ourselves.
|
||||
fn get_git_hash() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let workspace_root = workspace_root();
|
||||
let gitdir = workspace_root.join(".git");
|
||||
let jjdir = workspace_root.join(".jj");
|
||||
let commit_id = if gitdir.exists() {
|
||||
// .git/HEAD contains ref: refs/heads/branch
|
||||
let headpath = gitdir.join("HEAD");
|
||||
let headstr = read_to_string(headpath)?;
|
||||
let headref = headstr.split(' ').nth(1).unwrap().trim();
|
||||
|
||||
// .git/refs/heads/branch contains the SHA
|
||||
let refpath = gitdir.join(headref);
|
||||
// Shorten to 9 characters (what git describe does currently)
|
||||
read_to_string(refpath)?
|
||||
} else if jjdir.exists() {
|
||||
let output = Command::new("jj")
|
||||
.args([
|
||||
"log",
|
||||
"--revisions",
|
||||
"@",
|
||||
"--no-graph",
|
||||
"--ignore-working-copy",
|
||||
"--template",
|
||||
"commit_id",
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
String::from_utf8_lossy(&output.stdout).to_string()
|
||||
} else {
|
||||
return Err("did not find either of .git or .jj".into());
|
||||
};
|
||||
let refstr = &commit_id[0..9];
|
||||
let refstr = refstr.trim();
|
||||
|
||||
let version = env!("CARGO_PKG_VERSION").to_owned();
|
||||
Ok(version + "-g" + refstr)
|
||||
}
|
||||
|
||||
get_git_hash().expect("Could not get a version. Either set $FISH_BUILD_VERSION or install git.")
|
||||
String::from_utf8(
|
||||
Command::new("build_tools/git_version_gen.sh")
|
||||
.output()
|
||||
.unwrap()
|
||||
.stdout,
|
||||
)
|
||||
.unwrap()
|
||||
.trim_ascii_end()
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
@@ -33,8 +33,13 @@ fi
|
||||
cargo() {
|
||||
subcmd=$1
|
||||
shift
|
||||
# shellcheck disable=2086
|
||||
command cargo "$subcmd" $cargo_args "$@"
|
||||
if [ -n "$FISH_CHECK_RUST_TOOLCHAIN" ]; then
|
||||
# shellcheck disable=2086
|
||||
command cargo "+$FISH_CHECK_RUST_TOOLCHAIN" "$subcmd" $cargo_args "$@"
|
||||
else
|
||||
# shellcheck disable=2086
|
||||
command cargo "$subcmd" $cargo_args "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup () {
|
||||
|
||||
@@ -1,70 +1,22 @@
|
||||
#!/bin/sh
|
||||
# Originally from the git sources (GIT-VERSION-GEN)
|
||||
# Presumably (C) Junio C Hamano <junkio@cox.net>
|
||||
# Reused under GPL v2.0
|
||||
# Modified for fish by David Adam <zanchey@ucc.gu.uwa.edu.au>
|
||||
|
||||
set -e
|
||||
|
||||
# Find the fish directory as two levels up from script directory.
|
||||
FISH_BASE_DIR="$( cd "$( dirname "$( dirname "$0" )" )" && pwd )"
|
||||
DEF_VER=unknown
|
||||
git_permission_failed=0
|
||||
|
||||
# First see if there is a version file (included in release tarballs),
|
||||
# then try git-describe, then default.
|
||||
if test -f version
|
||||
then
|
||||
VN=$(cat version) || VN="$DEF_VER"
|
||||
else
|
||||
if VN=$(git -C "$FISH_BASE_DIR" describe --always --dirty 2>/dev/null); then
|
||||
:
|
||||
version=$(
|
||||
awk <"$FISH_BASE_DIR/Cargo.toml" -F'"' '$1 == "version = " { print $2 }'
|
||||
)
|
||||
if git_version=$(
|
||||
GIT_CEILING_DIRECTORIES=$FISH_BASE_DIR/.. \
|
||||
git -C "$FISH_BASE_DIR" describe --always --dirty 2>/dev/null); then
|
||||
if [ "$git_version" = "${git_version#"$version"}" ]; then
|
||||
version=$version-g$git_version
|
||||
else
|
||||
if test $? = 128; then
|
||||
# Current git versions return status 128
|
||||
# when run in a repo owned by another user.
|
||||
# Even for describe and everything.
|
||||
# This occurs for `sudo make install`.
|
||||
git_permission_failed=1
|
||||
fi
|
||||
VN="$DEF_VER"
|
||||
version=$git_version
|
||||
fi
|
||||
fi
|
||||
|
||||
# If the first param is --stdout, then output to stdout and exit.
|
||||
if test "$1" = '--stdout'
|
||||
then
|
||||
echo $VN
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Set the output directory as either the first param or cwd.
|
||||
test -n "$1" && OUTPUT_DIR=$1/ || OUTPUT_DIR=
|
||||
FBVF="${OUTPUT_DIR}FISH-BUILD-VERSION-FILE"
|
||||
|
||||
if test "$VN" = unknown && test -r "$FBVF" && test "$git_permission_failed" = 1
|
||||
then
|
||||
# HACK: Git failed, so we keep the current version file.
|
||||
# This helps in case you built fish as a normal user
|
||||
# and then try to `sudo make install` it.
|
||||
date +%s > "${OUTPUT_DIR}"fish-build-version-witness.txt
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -r "$FBVF"
|
||||
then
|
||||
VC=$(cat "$FBVF")
|
||||
else
|
||||
VC="unset"
|
||||
fi
|
||||
|
||||
# Maybe output the FBVF
|
||||
# It looks like "2.7.1-621-ga2f065e6"
|
||||
test "$VN" = "$VC" || {
|
||||
echo >&2 "$VN"
|
||||
echo "$VN" >"$FBVF"
|
||||
}
|
||||
|
||||
# Output the fish-build-version-witness.txt
|
||||
# See https://cmake.org/cmake/help/v3.4/policy/CMP0058.html
|
||||
date +%s > "${OUTPUT_DIR}"fish-build-version-witness.txt
|
||||
echo "$version"
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# LSAN can detect leaks tracing back to __asan::AsanThread::ThreadStart (probably caused by our
|
||||
# threads not exiting before their TLS dtors are called). Just ignore it.
|
||||
leak:AsanThread
|
||||
|
||||
@@ -25,7 +25,7 @@ NOTARIZE=
|
||||
|
||||
ARM64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=11.0'
|
||||
X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.12'
|
||||
cmake_args=
|
||||
cmake_args=()
|
||||
|
||||
while getopts "c:sf:i:p:e:nj:" opt; do
|
||||
case $opt in
|
||||
@@ -49,7 +49,7 @@ if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
VERSION=$(build_tools/git_version_gen.sh)
|
||||
|
||||
echo "Version is $VERSION"
|
||||
|
||||
@@ -82,17 +82,16 @@ do_cmake() {
|
||||
&& env DESTDIR="$PKGDIR/root/" $ARM64_DEPLOY_TARGET make install;
|
||||
}
|
||||
|
||||
# Build for x86-64 but do not install; instead we will make some fat binaries inside the root.
|
||||
# Build for x86-64 but do not install; instead we will make a fat binary inside the root.
|
||||
{ cd "$PKGDIR/build_x86_64" \
|
||||
&& do_cmake -DRust_CARGO_TARGET=x86_64-apple-darwin \
|
||||
&& env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; }
|
||||
|
||||
# Fatten them up.
|
||||
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
chmod 755 "$FILE"
|
||||
done
|
||||
# Fatten it up.
|
||||
FILE=$PKGDIR/root/usr/local/bin/fish
|
||||
X86_FILE=$PKGDIR/build_x86_64/$(basename "$FILE")
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
chmod 755 "$FILE"
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing executables"
|
||||
@@ -105,9 +104,7 @@ if test -n "$SIGN"; then
|
||||
if [ -n "$ENTITLEMENTS_FILE" ]; then
|
||||
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
|
||||
fi
|
||||
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "$FILE")
|
||||
done
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "$PKGDIR"/root/usr/local/bin/fish)
|
||||
fi
|
||||
|
||||
pkgbuild --scripts "$SRC_DIR/build_tools/osx_package_scripts" --root "$PKGDIR/root/" --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" "$PKGDIR/intermediates/fish.pkg"
|
||||
@@ -128,15 +125,13 @@ fi
|
||||
(cd "$PKGDIR/build_arm64" && env $ARM64_DEPLOY_TARGET make -j 12 fish_macapp)
|
||||
(cd "$PKGDIR/build_x86_64" && env $X86_64_DEPLOY_TARGET make -j 12 fish_macapp)
|
||||
|
||||
# Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
|
||||
# Make the app's /usr/local/bin/fish binary universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
|
||||
cd "$PKGDIR/build_arm64"
|
||||
for FILE in fish.app/Contents/Resources/base/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
|
||||
# macho-universal-create screws up the permissions.
|
||||
chmod 755 "$FILE"
|
||||
done
|
||||
FILE=fish.app/Contents/Resources/base/usr/local/bin/fish
|
||||
X86_FILE=$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename "$FILE")
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
# macho-universal-create screws up the permissions.
|
||||
chmod 755 "$FILE"
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing app"
|
||||
|
||||
@@ -3,21 +3,41 @@
|
||||
# Script to generate a tarball
|
||||
# Outputs to $FISH_ARTEFACT_PATH or ~/fish_built by default
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# Get the version
|
||||
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
VERSION=$(build_tools/git_version_gen.sh)
|
||||
|
||||
prefix=fish-$VERSION
|
||||
path=${FISH_ARTEFACT_PATH:-~/fish_built}/$prefix.tar.xz
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
manifest=$tmpdir/Cargo.toml
|
||||
lockfile=$tmpdir/Cargo.lock
|
||||
|
||||
sed "s/^version = \".*\"\$/version = \"$VERSION\"/g" Cargo.toml \
|
||||
>"$manifest"
|
||||
awk -v version=$VERSION '
|
||||
/^name = "fish"$/ { ok=1 }
|
||||
ok == 1 && /^version = ".*"$/ {
|
||||
ok = 2;
|
||||
$0 = "version = \"" version "\"";
|
||||
}
|
||||
{print}
|
||||
' \
|
||||
Cargo.lock >"$lockfile"
|
||||
|
||||
git archive \
|
||||
--prefix="$prefix/" \
|
||||
--add-virtual-file="$prefix/version:$VERSION" \
|
||||
--add-virtual-file="$prefix/Cargo.toml:$(cat "$manifest")" \
|
||||
--add-virtual-file="$prefix/Cargo.lock:$(cat "$lockfile")" \
|
||||
HEAD |
|
||||
xz >"$path"
|
||||
|
||||
rm "$manifest"
|
||||
rm "$lockfile"
|
||||
rmdir "$tmpdir"
|
||||
|
||||
# Output what we did, and the sha256 hash
|
||||
echo "Tarball written to $path"
|
||||
openssl dgst -sha256 "$path"
|
||||
|
||||
@@ -26,7 +26,7 @@ fi
|
||||
wd="$PWD"
|
||||
|
||||
# Get the version from git-describe
|
||||
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
VERSION=$(build_tools/git_version_gen.sh)
|
||||
|
||||
# The name of the prefix, which is the directory that you get when you untar
|
||||
prefix="fish-$VERSION"
|
||||
@@ -42,8 +42,14 @@ rm -f "$path" "$path".xz
|
||||
PREFIX_TMPDIR=$(mktemp -d)
|
||||
cd "$PREFIX_TMPDIR"
|
||||
|
||||
# Add .cargo/config.toml. This means that the caller may need to remove that file from the tarball.
|
||||
# See e4674cd7b5f (.cargo/config.toml: exclude from tarball, 2025-01-12)
|
||||
|
||||
mkdir .cargo
|
||||
cargo vendor --manifest-path "$wd/Cargo.toml" > .cargo/config.toml
|
||||
{
|
||||
cat "$wd"/.cargo/config.toml
|
||||
cargo vendor --manifest-path "$wd/Cargo.toml"
|
||||
} > .cargo/config.toml
|
||||
|
||||
tar cfvJ "$path".xz vendor .cargo
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Helvetica Neue", sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
code, tt {
|
||||
font-family: ui-monospace, Menlo, monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<strong>fish</strong> is a smart and user-friendly command line shell. For more information, visit <a href="https://fishshell.com">fishshell.com</a>.
|
||||
</p>
|
||||
<p>
|
||||
<strong>fish</strong> will be installed into <tt>/usr/local/</tt>, and its path will be added to <wbr><tt>/etc/shells</tt> if necessary.
|
||||
</p>
|
||||
<p>
|
||||
Your default shell will <em>not</em> be changed. To make <strong>fish</strong> your login shell after the installation, run:
|
||||
</p>
|
||||
<p>
|
||||
<code>chsh -s /usr/local/bin/fish</code>
|
||||
</p>
|
||||
<p>Enjoy! Bugs can be reported on <a href="https://github.org/fish-shell/fish-shell/">GitHub</a>.</p>
|
||||
</body>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Helvetica Neue", sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
code, tt {
|
||||
font-family: ui-monospace, Menlo, monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<strong>fish</strong> is a smart and user-friendly command line shell. For more information, visit <a href="https://fishshell.com">fishshell.com</a>.
|
||||
</p>
|
||||
<p>
|
||||
<strong>fish</strong> will be installed into <tt>/usr/local/</tt>, and its path will be added to <wbr><tt>/etc/shells</tt> if necessary.
|
||||
</p>
|
||||
<p>
|
||||
Your default shell will <em>not</em> be changed. To make <strong>fish</strong> your login shell after the installation, run:
|
||||
</p>
|
||||
<p>
|
||||
<code>chsh -s /usr/local/bin/fish</code>
|
||||
</p>
|
||||
<p>Enjoy! Bugs can be reported on <a href="https://github.com/fish-shell/fish-shell/">GitHub</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
if test $# -eq 0
|
||||
then
|
||||
echo "usage: $0 shellname [shellname ...]"
|
||||
exit 1
|
||||
echo "usage: $0 shellname [shellname ...]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
scriptname=$(basename "$0")
|
||||
|
||||
@@ -83,9 +83,15 @@ sed -i \
|
||||
-e "2c$(printf %s "$changelog_title" | sed s/./=/g)" \
|
||||
CHANGELOG.rst
|
||||
|
||||
CommitVersion() {
|
||||
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
|
||||
cargo fetch --offline
|
||||
CreateCommit() {
|
||||
git commit -m "$1
|
||||
|
||||
Created by ./build_tools/release.sh $version"
|
||||
}
|
||||
|
||||
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
|
||||
cargo fetch --offline # bumps the version in Cargo.lock
|
||||
if [ "$1" = "$version" ]; then
|
||||
# debchange is a Debian script to manage the Debian changelog, but
|
||||
# it's too annoying to install everywhere. Just do it by hand.
|
||||
cat - contrib/debian/changelog > contrib/debian/changelog.new <<EOF
|
||||
@@ -99,14 +105,13 @@ fish (${version}-1) stable; urgency=medium
|
||||
|
||||
EOF
|
||||
mv contrib/debian/changelog.new contrib/debian/changelog
|
||||
git add CHANGELOG.rst Cargo.toml Cargo.lock contrib/debian/changelog
|
||||
git commit -m "$2
|
||||
|
||||
Created by ./build_tools/release.sh $version"
|
||||
}
|
||||
|
||||
CommitVersion "$version" "Release $version"
|
||||
git add contrib/debian/changelog
|
||||
fi
|
||||
git add CHANGELOG.rst Cargo.toml Cargo.lock
|
||||
CreateCommit "Release $version"
|
||||
|
||||
# Tags must be full objects, not lightweight tags, for
|
||||
# git_version-gen.sh to work.
|
||||
git -c "user.signingKey=$committer" \
|
||||
tag --sign --message="Release $version" $version
|
||||
|
||||
@@ -174,7 +179,7 @@ actual_tag_oid=$(git ls-remote "$remote" |
|
||||
)
|
||||
CopyDocs() {
|
||||
rm -rf "$fish_site/site/docs/$1"
|
||||
cp -r "$tmpdir/local-tarball/fish-$version/user_doc/html" "$fish_site/site/docs/$1"
|
||||
cp -r "$tmpdir/local-tarball/fish-$version/cargo/fish-docs/html" "$fish_site/site/docs/$1"
|
||||
git -C $fish_site add "site/docs/$1"
|
||||
}
|
||||
minor_version=${version%.*}
|
||||
@@ -276,7 +281,8 @@ fish ?.?.? (released ???)
|
||||
EOF
|
||||
)
|
||||
printf %s\\n "$changelog" >CHANGELOG.rst
|
||||
CommitVersion ${version}-snapshot "start new cycle"
|
||||
git add CHANGELOG.rst
|
||||
CreateCommit "start new cycle"
|
||||
git push $remote HEAD:master
|
||||
} fi
|
||||
|
||||
@@ -296,6 +302,8 @@ milestone_number() {
|
||||
gh_api_repo milestones/"$(milestone_number "$milestone_version")" \
|
||||
--method PATCH --raw-field state=closed
|
||||
|
||||
next_minor_version=$(echo "$minor_version" |
|
||||
awk -F. '{ printf "%s.%s", $1, $2+1 }')
|
||||
if [ -z "$(milestone_number "$next_minor_version")" ]; then
|
||||
gh_api_repo milestones --method POST \
|
||||
--raw-field title="fish $next_minor_version"
|
||||
|
||||
@@ -24,7 +24,7 @@ if set -l -q _flag_all
|
||||
end
|
||||
end
|
||||
|
||||
set -l workspace_root (status dirname)/..
|
||||
set -l workspace_root (realpath (status dirname)/..)
|
||||
|
||||
if test $all = yes
|
||||
if not set -l -q _flag_force; and not set -l -q _flag_check
|
||||
|
||||
@@ -10,22 +10,49 @@ command -v updatecli
|
||||
command -v uv
|
||||
sort --version-sort </dev/null
|
||||
|
||||
uv lock --check
|
||||
# TODO This is copied from .github/actions/install-sphinx/action.yml
|
||||
uv lock --check --exclude-newer="$(awk -F'"' <uv.lock '/^exclude-newer[[:space:]]*=/ {print $2}')"
|
||||
|
||||
update_gh_action() {
|
||||
repo=$1
|
||||
version=$(curl -fsS "https://api.github.com/repos/$repo/releases/latest" | jq -r .tag_name)
|
||||
[ -n "$version" ]
|
||||
tag_oid=$(git ls-remote "https://github.com/$repo.git" "refs/tags/$version" | cut -f1)
|
||||
[ -n "$tag_oid" ]
|
||||
find .github/workflows -name '*.yml' -type f -exec \
|
||||
sed -i "s|uses: $repo@\S\+\( \+#.*\)\?|\
|
||||
uses: $repo@$tag_oid # $version, build_tools/update-dependencies.sh|g" {} +
|
||||
}
|
||||
update_gh_action actions/checkout
|
||||
update_gh_action actions/github-script
|
||||
update_gh_action actions/upload-artifact
|
||||
update_gh_action actions/download-artifact
|
||||
update_gh_action docker/login-action
|
||||
update_gh_action docker/build-push-action
|
||||
update_gh_action docker/metadata-action
|
||||
update_gh_action EmbarkStudios/cargo-deny-action
|
||||
update_gh_action dessant/lock-threads
|
||||
update_gh_action softprops/action-gh-release
|
||||
update_gh_action msys2/setup-msys2
|
||||
|
||||
updatecli "${@:-apply}"
|
||||
|
||||
uv lock # Python version constraints may have changed.
|
||||
# Python version constraints may have changed.
|
||||
uv lock --upgrade --exclude-newer="$(date --date='7 days ago' --iso-8601)"
|
||||
|
||||
from_gh() {
|
||||
repo=$1
|
||||
path=$2
|
||||
out_dir=$3
|
||||
destination=$3
|
||||
contents=$(curl -fsS https://raw.githubusercontent.com/"${repo}"/refs/heads/master/"${path}")
|
||||
printf '%s\n' >"$out_dir/$(basename "$path")" "$contents"
|
||||
printf '%s\n' >"$destination" "$contents"
|
||||
}
|
||||
from_gh ridiculousfish/widecharwidth widechar_width.rs crates/widecharwidth/src/
|
||||
from_gh ridiculousfish/littlecheck littlecheck/littlecheck.py tests/
|
||||
|
||||
from_gh ridiculousfish/widecharwidth widechar_width.rs crates/widecharwidth/src/widechar_width.rs
|
||||
from_gh ridiculousfish/littlecheck littlecheck/littlecheck.py tests/littlecheck.py
|
||||
from_gh catppuccin/fish 'themes/Catppuccin Frappe.theme' share/themes/catppuccin-frappe.theme
|
||||
from_gh catppuccin/fish 'themes/Catppuccin Macchiato.theme' share/themes/catppuccin-macchiato.theme
|
||||
from_gh catppuccin/fish 'themes/Catppuccin Mocha.theme' share/themes/catppuccin-mocha.theme
|
||||
|
||||
# Update Cargo.lock
|
||||
cargo update
|
||||
|
||||
@@ -1,42 +1,32 @@
|
||||
find_program(SPHINX_EXECUTABLE NAMES sphinx-build
|
||||
HINTS
|
||||
$ENV{SPHINX_DIR}
|
||||
PATH_SUFFIXES bin
|
||||
DOC "Sphinx documentation generator")
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
set(SPHINX_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/doc_src")
|
||||
set(SPHINX_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/user_doc")
|
||||
set(SPHINX_BUILD_DIR "${SPHINX_ROOT_DIR}/build")
|
||||
set(SPHINX_HTML_DIR "${SPHINX_ROOT_DIR}/html")
|
||||
set(SPHINX_MANPAGE_DIR "${SPHINX_ROOT_DIR}/man")
|
||||
set(SPHINX_OUTPUT_DIR "${FISH_RUST_BUILD_DIR}/fish-docs")
|
||||
|
||||
set(FISH_INDENT_FOR_BUILDING_DOCS "" CACHE FILEPATH "Path to fish_indent executable for building HTML docs")
|
||||
|
||||
if(FISH_INDENT_FOR_BUILDING_DOCS)
|
||||
set(SPHINX_HTML_FISH_INDENT_DEP)
|
||||
else()
|
||||
set(FISH_INDENT_FOR_BUILDING_DOCS "${CMAKE_CURRENT_BINARY_DIR}/fish_indent")
|
||||
set(SPHINX_HTML_FISH_INDENT_DEP fish_indent)
|
||||
endif()
|
||||
|
||||
set(VARS_FOR_CARGO_SPHINX_WRAPPER
|
||||
"CARGO_TARGET_DIR=${FISH_RUST_BUILD_DIR}"
|
||||
"FISH_SPHINX=${SPHINX_EXECUTABLE}"
|
||||
)
|
||||
|
||||
# sphinx-docs uses fish_indent for highlighting.
|
||||
# Prepend the output dir of fish_indent to PATH.
|
||||
add_custom_target(sphinx-docs
|
||||
mkdir -p ${SPHINX_HTML_DIR}/_static/
|
||||
COMMAND env PATH="${CMAKE_BINARY_DIR}:$$PATH"
|
||||
${SPHINX_EXECUTABLE}
|
||||
-j auto
|
||||
-q -b html
|
||||
-c "${SPHINX_SRC_DIR}"
|
||||
-d "${SPHINX_ROOT_DIR}/.doctrees-html"
|
||||
"${SPHINX_SRC_DIR}"
|
||||
"${SPHINX_HTML_DIR}"
|
||||
DEPENDS ${SPHINX_SRC_DIR}/fish_indent_lexer.py fish_indent
|
||||
COMMAND env ${VARS_FOR_CARGO_SPHINX_WRAPPER}
|
||||
${Rust_CARGO} xtask html-docs --fish-indent=${FISH_INDENT_FOR_BUILDING_DOCS}
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
DEPENDS ${SPHINX_HTML_FISH_INDENT_DEP}
|
||||
COMMENT "Building HTML documentation with Sphinx")
|
||||
|
||||
add_custom_target(sphinx-manpages
|
||||
env FISH_BUILD_VERSION_FILE=${CMAKE_CURRENT_BINARY_DIR}/${FBVF}
|
||||
${SPHINX_EXECUTABLE}
|
||||
-j auto
|
||||
-q -b man
|
||||
-c "${SPHINX_SRC_DIR}"
|
||||
-d "${SPHINX_ROOT_DIR}/.doctrees-man"
|
||||
"${SPHINX_SRC_DIR}"
|
||||
"${SPHINX_MANPAGE_DIR}/man1"
|
||||
DEPENDS CHECK-FISH-BUILD-VERSION-FILE
|
||||
COMMAND env ${VARS_FOR_CARGO_SPHINX_WRAPPER}
|
||||
${Rust_CARGO} xtask man-pages
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Building man pages with Sphinx")
|
||||
|
||||
if(NOT DEFINED WITH_DOCS) # Don't check for legacy options if the new one is defined, to help bisecting.
|
||||
@@ -61,7 +51,6 @@ endif()
|
||||
add_feature_info(Documentation WITH_DOCS "user manual and documentation")
|
||||
|
||||
if(WITH_DOCS)
|
||||
configure_file("${SPHINX_SRC_DIR}/conf.py" "${SPHINX_BUILD_DIR}/conf.py" @ONLY)
|
||||
add_custom_target(doc ALL
|
||||
DEPENDS sphinx-docs sphinx-manpages)
|
||||
# Group docs targets into a DocsTargets folder
|
||||
|
||||
@@ -28,18 +28,18 @@ set(extra_confdir
|
||||
|
||||
|
||||
# These are the man pages that go in system manpath; all manpages go in the fish-specific manpath.
|
||||
set(MANUALS ${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish_indent.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish_key_reader.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-doc.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-tutorial.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-language.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-interactive.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-terminal-compatibility.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-completions.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-prompt-tutorial.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-for-bash-users.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/fish-faq.1
|
||||
set(MANUALS ${SPHINX_OUTPUT_DIR}/man/man1/fish.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish_indent.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish_key_reader.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-doc.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-tutorial.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-language.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-interactive.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-terminal-compatibility.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-completions.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-prompt-tutorial.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-for-bash-users.1
|
||||
${SPHINX_OUTPUT_DIR}/man/man1/fish-faq.1
|
||||
)
|
||||
|
||||
# Determine which man page we don't want to install.
|
||||
@@ -114,9 +114,9 @@ configure_file(fish.pc.in fish.pc.noversion @ONLY)
|
||||
add_custom_command(OUTPUT fish.pc
|
||||
COMMAND sed '/Version/d' fish.pc.noversion > fish.pc
|
||||
COMMAND printf "Version: " >> fish.pc
|
||||
COMMAND cat ${FBVF} >> fish.pc
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh >> fish.pc
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS CHECK-FISH-BUILD-VERSION-FILE ${CMAKE_CURRENT_BINARY_DIR}/fish.pc.noversion)
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/fish.pc.noversion)
|
||||
|
||||
add_custom_target(build_fish_pc ALL DEPENDS fish.pc)
|
||||
|
||||
@@ -131,9 +131,17 @@ install(DIRECTORY share/functions/
|
||||
DESTINATION ${rel_datadir}/fish/functions
|
||||
FILES_MATCHING PATTERN "*.fish")
|
||||
|
||||
install(DIRECTORY share/prompts/
|
||||
DESTINATION ${rel_datadir}/fish/prompts
|
||||
FILES_MATCHING PATTERN "*.fish")
|
||||
|
||||
install(DIRECTORY share/themes/
|
||||
DESTINATION ${rel_datadir}/fish/themes
|
||||
FILES_MATCHING PATTERN "*.theme")
|
||||
|
||||
# CONDEMNED_PAGE is managed by the conditional above
|
||||
# Building the man pages is optional: if sphinx isn't installed, they're not built
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/user_doc/man/man1/
|
||||
install(DIRECTORY ${SPHINX_OUTPUT_DIR}/man/man1/
|
||||
DESTINATION ${rel_datadir}/fish/man/man1
|
||||
FILES_MATCHING
|
||||
PATTERN "*.1"
|
||||
@@ -149,18 +157,16 @@ install(DIRECTORY share/tools/web_config
|
||||
PATTERN "*.css"
|
||||
PATTERN "*.html"
|
||||
PATTERN "*.py"
|
||||
PATTERN "*.js"
|
||||
PATTERN "*.theme"
|
||||
PATTERN "*.fish")
|
||||
PATTERN "*.js")
|
||||
|
||||
# Building the man pages is optional: if Sphinx isn't installed, they're not built
|
||||
install(FILES ${MANUALS} DESTINATION ${mandir}/man1/ OPTIONAL)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/user_doc/html/ # Trailing slash is important!
|
||||
install(DIRECTORY ${SPHINX_OUTPUT_DIR}/html/ # Trailing slash is important!
|
||||
DESTINATION ${docdir} OPTIONAL)
|
||||
install(FILES CHANGELOG.rst DESTINATION ${docdir})
|
||||
|
||||
# Group install targets into a InstallTargets folder
|
||||
set_property(TARGET build_fish_pc CHECK-FISH-BUILD-VERSION-FILE
|
||||
set_property(TARGET build_fish_pc
|
||||
PROPERTY FOLDER cmake/InstallTargets)
|
||||
|
||||
# Make a target build_root that installs into the buildroot directory, for testing.
|
||||
|
||||
@@ -26,7 +26,7 @@ add_executable(fish_macapp EXCLUDE_FROM_ALL
|
||||
# so cmake must be re-run after version changes for the app to be updated. But
|
||||
# generally this will be run by make_macos_pkg.sh which always re-runs cmake.
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh --stdout
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh
|
||||
COMMAND cut -d- -f1
|
||||
OUTPUT_VARIABLE FISH_SHORT_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
include(FeatureSummary)
|
||||
include(FindRust)
|
||||
find_package(Rust REQUIRED)
|
||||
|
||||
set(FISH_RUST_BUILD_DIR "${CMAKE_BINARY_DIR}/cargo/build")
|
||||
set(FISH_RUST_BUILD_DIR "${CMAKE_BINARY_DIR}/cargo")
|
||||
|
||||
if(DEFINED ASAN)
|
||||
list(APPEND CARGO_FLAGS "-Z" "build-std")
|
||||
list(APPEND FISH_CARGO_FEATURES_LIST "asan")
|
||||
endif()
|
||||
if(DEFINED TSAN)
|
||||
list(APPEND CARGO_FLAGS "-Z" "build-std")
|
||||
@@ -20,10 +20,17 @@ endif()
|
||||
|
||||
set(rust_profile $<IF:$<CONFIG:Debug>,debug,$<IF:$<CONFIG:RelWithDebInfo>,release-with-debug,release>>)
|
||||
|
||||
option(WITH_GETTEXT "Build with gettext localization support. Requires `msgfmt` to work." ON)
|
||||
if (NOT DEFINED WITH_MESSAGE_LOCALIZATION) # Don't check for legacy options if the new one is defined, to help bisecting.
|
||||
if(DEFINED WITH_GETTEXT)
|
||||
message(FATAL_ERROR "the WITH_GETTEXT option is no longer supported, use -DWITH_MESSAGE_LOCALIZATION=ON|OFF")
|
||||
endif()
|
||||
endif()
|
||||
option(WITH_MESSAGE_LOCALIZATION "Build with localization support. Requires `msgfmt` to work." ON)
|
||||
# Enable gettext feature unless explicitly disabled.
|
||||
if(NOT DEFINED WITH_GETTEXT OR "${WITH_GETTEXT}")
|
||||
if(NOT DEFINED WITH_MESSAGE_LOCALIZATION OR "${WITH_MESSAGE_LOCALIZATION}")
|
||||
list(APPEND FISH_CARGO_FEATURES_LIST "localize-messages")
|
||||
endif()
|
||||
|
||||
add_feature_info(Translation WITH_MESSAGE_LOCALIZATION "message localization (requires gettext)")
|
||||
|
||||
list(JOIN FISH_CARGO_FEATURES_LIST , FISH_CARGO_FEATURES)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
add_executable(fish_test_helper tests/fish_test_helper.c)
|
||||
|
||||
FILE(GLOB FISH_CHECKS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/checks/*.fish)
|
||||
foreach(CHECK ${FISH_CHECKS})
|
||||
get_filename_component(CHECK_NAME ${CHECK} NAME)
|
||||
@@ -8,7 +6,7 @@ foreach(CHECK ${FISH_CHECKS})
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
|
||||
checks/${CHECK_NAME}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
DEPENDS fish fish_indent fish_key_reader fish_test_helper
|
||||
DEPENDS fish fish_indent fish_key_reader
|
||||
USES_TERMINAL
|
||||
)
|
||||
endforeach(CHECK)
|
||||
@@ -21,7 +19,7 @@ foreach(PEXPECT ${PEXPECTS})
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
|
||||
pexpects/${PEXPECT}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
DEPENDS fish fish_indent fish_key_reader fish_test_helper
|
||||
DEPENDS fish fish_indent fish_key_reader
|
||||
USES_TERMINAL
|
||||
)
|
||||
endforeach(PEXPECT)
|
||||
@@ -67,6 +65,6 @@ add_custom_target(fish_run_tests
|
||||
--target-dir ${rust_target_dir}
|
||||
${cargo_test_flags}
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
DEPENDS fish fish_indent fish_key_reader fish_test_helper
|
||||
DEPENDS fish fish_indent fish_key_reader
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
# This file adds commands to manage the FISH-BUILD-VERSION-FILE (hereafter
|
||||
# FBVF). This file exists in the build directory and is used to populate the
|
||||
# documentation and also the version string in fish_version.o (printed with
|
||||
# `echo $version` and also fish --version). The essential idea is that we are
|
||||
# going to invoke git_version_gen.sh, which will update the
|
||||
# FISH-BUILD-VERSION-FILE only if it needs to change; this is what makes
|
||||
# incremental rebuilds fast.
|
||||
#
|
||||
# This code is delicate, with the chief subtlety revolving around Ninja. A
|
||||
# natural and naive approach would tell the generated build system that FBVF is
|
||||
# a dependency of fish_version.o, and that git_version_gen.sh updates it. Make
|
||||
# will then invoke the script, check the timestamp on fish_version.o and FBVF,
|
||||
# see that FBVF is earlier, and then not rebuild fish_version.o. Ninja,
|
||||
# however, decides what to build up-front and will unconditionally rebuild
|
||||
# fish_version.o.
|
||||
#
|
||||
# To avoid this with Ninja, we want to hook into its 'restat' option which we
|
||||
# can do through the BYPRODUCTS feature of CMake. See
|
||||
# https://cmake.org/cmake/help/latest/policy/CMP0058.html
|
||||
#
|
||||
# Unfortunately BYPRODUCTS behaves strangely with the Makefile generator: it
|
||||
# marks FBVF as generated and then CMake itself will `touch` it on every build,
|
||||
# meaning that using BYPRODUCTS will cause fish_version.o to be rebuilt
|
||||
# unconditionally with the Makefile generator. Thus we want to use the
|
||||
# natural-and-naive approach for Makefiles.
|
||||
|
||||
# **IMPORTANT** If you touch these build rules, please test both Ninja and
|
||||
# Makefile generators with both a clean and dirty git tree. Verify that both
|
||||
# generated build systems rebuild fish when the git tree goes from dirty to
|
||||
# clean (and vice versa), and verify they do NOT rebuild it when the git tree
|
||||
# stays the same (incremental builds must be fast).
|
||||
|
||||
# Just a handy abbreviation.
|
||||
set(FBVF FISH-BUILD-VERSION-FILE)
|
||||
|
||||
# TODO: find a cleaner way to do this.
|
||||
IF (${CMAKE_GENERATOR} STREQUAL Ninja)
|
||||
set(FBVF-OUTPUT fish-build-version-witness.txt)
|
||||
set(CFBVF-BYPRODUCTS ${FBVF})
|
||||
else(${CMAKE_GENERATOR} STREQUAL Ninja)
|
||||
set(FBVF-OUTPUT ${FBVF})
|
||||
set(CFBVF-BYPRODUCTS)
|
||||
endif(${CMAKE_GENERATOR} STREQUAL Ninja)
|
||||
|
||||
# Set up the version targets
|
||||
add_custom_target(CHECK-FISH-BUILD-VERSION-FILE
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh ${CMAKE_CURRENT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
BYPRODUCTS ${CFBVF-BYPRODUCTS})
|
||||
|
||||
add_custom_command(OUTPUT ${FBVF-OUTPUT}
|
||||
DEPENDS CHECK-FISH-BUILD-VERSION-FILE)
|
||||
|
||||
# Abbreviation for the target.
|
||||
set(CFBVF CHECK-FISH-BUILD-VERSION-FILE)
|
||||
@@ -1,3 +1,43 @@
|
||||
fish (4.5.0-1) stable; urgency=medium
|
||||
|
||||
* Release of new version 4.5.0.
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/4.5.0 for details.
|
||||
|
||||
-- Johannes Altmanninger <aclopte@gmail.com> Tue, 17 Feb 2026 11:32:33 +1100
|
||||
|
||||
fish (4.4.0-1) stable; urgency=medium
|
||||
|
||||
* Release of new version 4.4.0.
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/4.4.0 for details.
|
||||
|
||||
-- Johannes Altmanninger <aclopte@gmail.com> Tue, 03 Feb 2026 12:11:51 +1100
|
||||
|
||||
fish (4.3.3-1) stable; urgency=medium
|
||||
|
||||
* Release of new version 4.3.3.
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/4.3.3 for details.
|
||||
|
||||
-- Johannes Altmanninger <aclopte@gmail.com> Wed, 07 Jan 2026 08:34:20 +0100
|
||||
|
||||
fish (4.3.2-1) stable; urgency=medium
|
||||
|
||||
* Release of new version 4.3.2.
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/4.3.2 for details.
|
||||
|
||||
-- Johannes Altmanninger <aclopte@gmail.com> Tue, 30 Dec 2025 17:21:04 +0100
|
||||
|
||||
fish (4.3.1-1) stable; urgency=medium
|
||||
|
||||
* Release of new version 4.3.1.
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/4.3.1 for details.
|
||||
|
||||
-- Johannes Altmanninger <aclopte@gmail.com> Sun, 28 Dec 2025 16:54:44 +0100
|
||||
|
||||
fish (4.3.0-1) stable; urgency=medium
|
||||
|
||||
* Release of new version 4.3.0.
|
||||
|
||||
@@ -10,7 +10,7 @@ Build-Depends: debhelper-compat (= 13),
|
||||
gettext,
|
||||
libpcre2-dev,
|
||||
rustc (>= 1.85) | rustc-web (>= 1.85) | rustc-1.85,
|
||||
sphinx-doc,
|
||||
python3-sphinx,
|
||||
# Test dependencies
|
||||
locales-all,
|
||||
man-db,
|
||||
|
||||
@@ -34,7 +34,7 @@ Copyright: 1990-2007 Free Software Foundation, Inc.
|
||||
2022 fish-shell contributors
|
||||
License: GPL-2+
|
||||
|
||||
Files: src/wgetopt.rs
|
||||
Files: crates/wgetopt/src/wgetopt.rs
|
||||
Copyright: 1989-1994 Free Software Foundation, Inc.
|
||||
License: LGPL-2+
|
||||
|
||||
|
||||
@@ -16,14 +16,12 @@ export DEB_BUILD_MAINT_OPTIONS=optimize=-lto
|
||||
# Setting the build system is still required, because otherwise the GNUmakefile gets picked up
|
||||
override_dh_auto_configure:
|
||||
ln -s cargo-vendor/vendor vendor
|
||||
ln -s cargo-vendor/.cargo .cargo
|
||||
dh_auto_configure -- -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DRust_CARGO=$$(command -v cargo-1.85 || command -v cargo) \
|
||||
-DRust_COMPILER=$$(command -v rustc-1.85 || command -v rustc)
|
||||
|
||||
override_dh_clean:
|
||||
dh_clean --exclude=Cargo.toml.orig
|
||||
-unlink .cargo
|
||||
-unlink vendor
|
||||
|
||||
override_dh_auto_test:
|
||||
|
||||
3
contrib/debian/source/local-options
Normal file
3
contrib/debian/source/local-options
Normal file
@@ -0,0 +1,3 @@
|
||||
# The vendor tarball drops a new version of .cargo/config into place. Representing this as a patch
|
||||
# in automated workflows is tricky, so for our purposes auto-commit is fine.
|
||||
auto-commit
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{borrow::Cow, env, os::unix::ffi::OsStrExt, path::Path};
|
||||
use std::{borrow::Cow, env, os::unix::ffi::OsStrExt as _, path::Path};
|
||||
|
||||
pub fn env_var(name: &str) -> Option<String> {
|
||||
let err = match env::var(name) {
|
||||
@@ -34,6 +34,10 @@ pub fn fish_build_dir() -> Cow<'static, Path> {
|
||||
.unwrap_or(cargo_target_dir())
|
||||
}
|
||||
|
||||
pub fn fish_doc_dir() -> Cow<'static, Path> {
|
||||
cargo_target_dir().join("fish-docs").into()
|
||||
}
|
||||
|
||||
// TODO Move this to rsconf
|
||||
pub fn rebuild_if_path_changed<P: AsRef<Path>>(path: P) {
|
||||
rsconf::rebuild_if_path_changed(path.as_ref().to_str().unwrap());
|
||||
@@ -83,3 +87,18 @@ pub fn target_os_is_bsd() -> bool {
|
||||
pub fn target_os_is_cygwin() -> bool {
|
||||
target_os() == "cygwin"
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! as_os_strs {
|
||||
[ $( $x:expr, )* ] => {
|
||||
{
|
||||
use std::ffi::OsStr;
|
||||
fn as_os_str<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr {
|
||||
s.as_ref()
|
||||
}
|
||||
&[
|
||||
$( as_os_str($x), )*
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
use std::path::Path;
|
||||
|
||||
use fish_build_helper::{as_os_strs, fish_doc_dir};
|
||||
|
||||
fn main() {
|
||||
let man_dir = fish_build_helper::fish_build_dir().join("fish-man");
|
||||
let sec1_dir = man_dir.join("man1");
|
||||
let sec1_dir = fish_doc_dir().join("man").join("man1");
|
||||
// Running `cargo clippy` on a clean build directory panics, because when rust-embed
|
||||
// tries to embed a directory which does not exist it will panic.
|
||||
let _ = std::fs::create_dir_all(&sec1_dir);
|
||||
if !cfg!(clippy) {
|
||||
build_man(&man_dir, &sec1_dir);
|
||||
build_man(&sec1_dir);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_man(man_dir: &Path, sec1_dir: &Path) {
|
||||
fn build_man(sec1_dir: &Path) {
|
||||
use fish_build_helper::{env_var, workspace_root};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
let workspace_root = workspace_root();
|
||||
let doc_src_dir = workspace_root.join("doc_src");
|
||||
let doctrees_dir = fish_doc_dir().join(".doctrees-man");
|
||||
|
||||
fish_build_helper::rebuild_if_paths_changed([
|
||||
&workspace_root.join("CHANGELOG.rst"),
|
||||
@@ -27,36 +26,24 @@ fn build_man(man_dir: &Path, sec1_dir: &Path) {
|
||||
&doc_src_dir,
|
||||
]);
|
||||
|
||||
let args: &[&OsStr] = {
|
||||
fn as_os_str<S: AsRef<OsStr> + ?Sized>(s: &S) -> &OsStr {
|
||||
s.as_ref()
|
||||
}
|
||||
macro_rules! as_os_strs {
|
||||
( [ $( $x:expr, )* ] ) => {
|
||||
&[
|
||||
$( as_os_str($x), )*
|
||||
]
|
||||
}
|
||||
}
|
||||
as_os_strs!([
|
||||
"-j",
|
||||
"auto",
|
||||
"-q",
|
||||
"-b",
|
||||
"man",
|
||||
"-c",
|
||||
&doc_src_dir,
|
||||
// doctree path - put this *above* the man1 dir to exclude it.
|
||||
// this is ~6M
|
||||
"-d",
|
||||
&man_dir,
|
||||
&doc_src_dir,
|
||||
&sec1_dir,
|
||||
])
|
||||
};
|
||||
let args = as_os_strs![
|
||||
"-j",
|
||||
"auto",
|
||||
"-q",
|
||||
"-b",
|
||||
"man",
|
||||
"-c",
|
||||
&doc_src_dir,
|
||||
// doctree path - put this *above* the man1 dir to exclude it.
|
||||
// this is ~6M
|
||||
"-d",
|
||||
&doctrees_dir,
|
||||
&doc_src_dir,
|
||||
&sec1_dir,
|
||||
];
|
||||
|
||||
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
|
||||
if env_var("FISH_BUILD_DOCS") == Some("0".to_string()) {
|
||||
if env_var("FISH_BUILD_DOCS") == Some("0".to_owned()) {
|
||||
rsconf::warn!("Skipping man pages because $FISH_BUILD_DOCS is set to 0");
|
||||
return;
|
||||
}
|
||||
@@ -73,12 +60,12 @@ macro_rules! as_os_strs {
|
||||
.spawn()
|
||||
{
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
if env_var("FISH_BUILD_DOCS") == Some("1".to_string()) {
|
||||
panic!(
|
||||
"Could not find sphinx-build required to build man pages.\n\
|
||||
Install Sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0."
|
||||
);
|
||||
}
|
||||
assert_ne!(
|
||||
env_var("FISH_BUILD_DOCS"),
|
||||
Some("1".to_owned()),
|
||||
"Could not find sphinx-build required to build man pages.\n\
|
||||
Install Sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0."
|
||||
);
|
||||
rsconf::warn!(
|
||||
"Could not find sphinx-build required to build man pages. \
|
||||
If you install Sphinx now, you need to trigger a rebuild to include man pages. \
|
||||
@@ -105,9 +92,10 @@ macro_rules! as_os_strs {
|
||||
rsconf::warn!("sphinx-build: {}", String::from_utf8_lossy(&out.stderr));
|
||||
}
|
||||
assert_eq!(&String::from_utf8_lossy(&out.stdout), "");
|
||||
if !out.status.success() {
|
||||
panic!("sphinx-build failed to build the man pages.");
|
||||
}
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"sphinx-build failed to build the man pages."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
crates/color/Cargo.toml
Normal file
14
crates/color/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "fish-color"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.0.0"
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-common.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::prelude::*;
|
||||
use fish_common::assert_sorted_by_name;
|
||||
use fish_widestring::{L, wstr};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Color24 {
|
||||
@@ -321,8 +322,8 @@ fn term256_color_for_rgb(color: Color24) -> u8 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::color::{Color, Color24};
|
||||
use crate::prelude::*;
|
||||
use super::{Color, Color24};
|
||||
use fish_widestring::L;
|
||||
|
||||
#[test]
|
||||
fn parse() {
|
||||
@@ -342,9 +343,18 @@ fn parse() {
|
||||
#[test]
|
||||
fn parse_rgb() {
|
||||
assert!(Color::from_wstr(L!("##FF00A0")).is_none());
|
||||
assert!(Color::from_wstr(L!("#FF00A0")) == Some(Color::from_rgb(0xff, 0x00, 0xa0)));
|
||||
assert!(Color::from_wstr(L!("FF00A0")) == Some(Color::from_rgb(0xff, 0x00, 0xa0)));
|
||||
assert!(Color::from_wstr(L!("FAF")) == Some(Color::from_rgb(0xff, 0xaa, 0xff)));
|
||||
assert_eq!(
|
||||
Color::from_wstr(L!("#FF00A0")),
|
||||
Some(Color::from_rgb(0xff, 0x00, 0xa0))
|
||||
);
|
||||
assert_eq!(
|
||||
Color::from_wstr(L!("FF00A0")),
|
||||
Some(Color::from_rgb(0xff, 0x00, 0xa0))
|
||||
);
|
||||
assert_eq!(
|
||||
Color::from_wstr(L!("FAF")),
|
||||
Some(Color::from_rgb(0xff, 0xaa, 0xff))
|
||||
);
|
||||
}
|
||||
|
||||
// Regression test for multiplicative overflow in convert_color.
|
||||
@@ -7,9 +7,10 @@ repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
libc.workspace = true
|
||||
nix.workspace = true
|
||||
once_cell.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,35 +1,130 @@
|
||||
use libc::STDIN_FILENO;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::env;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use bitflags::bitflags;
|
||||
use fish_widestring::{L, char_offset, wstr};
|
||||
use libc::{SIG_IGN, SIGTTOU, STDIN_FILENO};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::io::Read;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::fd::{AsRawFd, BorrowedFd, RawFd};
|
||||
use std::os::unix::ffi::OsStrExt as _;
|
||||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
|
||||
use std::{env, mem, time};
|
||||
|
||||
// These are in the Unicode private-use range. We really shouldn't use this
|
||||
// range but have little choice in the matter given how our lexer/parser works.
|
||||
// We can't use non-characters for these two ranges because there are only 66 of
|
||||
// them and we need at least 256 + 64.
|
||||
//
|
||||
// If sizeof(wchar_t))==4 we could avoid using private-use chars; however, that
|
||||
// would result in fish having different behavior on machines with 16 versus 32
|
||||
// bit wchar_t. It's better that fish behave the same on both types of systems.
|
||||
//
|
||||
// Note: We don't use the highest 8 bit range (0xF800 - 0xF8FF) because we know
|
||||
// of at least one use of a codepoint in that range: the Apple symbol (0xF8FF)
|
||||
// on Mac OS X. See http://www.unicode.org/faq/private_use.html.
|
||||
pub const ENCODE_DIRECT_BASE: char = '\u{F600}';
|
||||
pub const ENCODE_DIRECT_END: char = char_offset(ENCODE_DIRECT_BASE, 256);
|
||||
pub const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
|
||||
pub const fn char_offset(base: char, offset: u32) -> char {
|
||||
match char::from_u32(base as u32 + offset) {
|
||||
Some(c) => c,
|
||||
None => panic!("not a valid char"),
|
||||
// Highest legal ASCII value.
|
||||
pub const ASCII_MAX: char = 127 as char;
|
||||
|
||||
// Highest legal 16-bit Unicode value.
|
||||
pub const UCS2_MAX: char = '\u{FFFF}';
|
||||
|
||||
// Highest legal byte value.
|
||||
pub const BYTE_MAX: char = 0xFF as char;
|
||||
|
||||
// Unicode BOM value.
|
||||
pub const UTF8_BOM_WCHAR: char = '\u{FEFF}';
|
||||
|
||||
// Use Unicode "non-characters" for internal characters as much as we can. This
|
||||
// gives us 32 "characters" for internal use that we can guarantee should not
|
||||
// appear in our input stream. See http://www.unicode.org/faq/private_use.html.
|
||||
pub const RESERVED_CHAR_BASE: char = '\u{FDD0}';
|
||||
pub const RESERVED_CHAR_END: char = '\u{FDF0}';
|
||||
// Split the available non-character values into two ranges to ensure there are
|
||||
// no conflicts among the places we use these special characters.
|
||||
pub const EXPAND_RESERVED_BASE: char = RESERVED_CHAR_BASE;
|
||||
pub const EXPAND_RESERVED_END: char = char_offset(EXPAND_RESERVED_BASE, 16);
|
||||
pub const WILDCARD_RESERVED_BASE: char = EXPAND_RESERVED_END;
|
||||
pub const WILDCARD_RESERVED_END: char = char_offset(WILDCARD_RESERVED_BASE, 16);
|
||||
// Make sure the ranges defined above don't exceed the range for non-characters.
|
||||
// This is to make sure we didn't do something stupid in subdividing the
|
||||
// Unicode range for our needs.
|
||||
const _: () = assert!(WILDCARD_RESERVED_END <= RESERVED_CHAR_END);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EscapeStringStyle {
|
||||
Script(EscapeFlags),
|
||||
Url,
|
||||
Var,
|
||||
Regex,
|
||||
}
|
||||
|
||||
impl Default for EscapeStringStyle {
|
||||
fn default() -> Self {
|
||||
Self::Script(EscapeFlags::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subslice_position<T: Eq>(a: &[T], b: &[T]) -> Option<usize> {
|
||||
if b.is_empty() {
|
||||
return Some(0);
|
||||
impl TryFrom<&wstr> for EscapeStringStyle {
|
||||
type Error = &'static wstr;
|
||||
fn try_from(s: &wstr) -> Result<Self, Self::Error> {
|
||||
use EscapeStringStyle::*;
|
||||
match s {
|
||||
s if s == "script" => Ok(Self::default()),
|
||||
s if s == "var" => Ok(Var),
|
||||
s if s == "url" => Ok(Url),
|
||||
s if s == "regex" => Ok(Regex),
|
||||
_ => Err(L!("Invalid escape style")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for the [`escape_string()`] function. These are only applicable when the escape style is
|
||||
/// [`EscapeStringStyle::Script`].
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct EscapeFlags: u32 {
|
||||
/// Do not escape special fish syntax characters like the semicolon. Only escape non-printable
|
||||
/// characters and backslashes.
|
||||
const NO_PRINTABLES = 1 << 0;
|
||||
/// Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty
|
||||
/// string.
|
||||
const NO_QUOTED = 1 << 1;
|
||||
/// Do not escape tildes.
|
||||
const NO_TILDE = 1 << 2;
|
||||
/// Replace non-printable control characters with Unicode symbols.
|
||||
const SYMBOLIC = 1 << 3;
|
||||
/// Escape ,
|
||||
const COMMA = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UnescapeStringStyle {
|
||||
Script(UnescapeFlags),
|
||||
Url,
|
||||
Var,
|
||||
}
|
||||
|
||||
impl Default for UnescapeStringStyle {
|
||||
fn default() -> Self {
|
||||
Self::Script(UnescapeFlags::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&wstr> for UnescapeStringStyle {
|
||||
type Error = &'static wstr;
|
||||
fn try_from(s: &wstr) -> Result<Self, Self::Error> {
|
||||
use UnescapeStringStyle::*;
|
||||
match s {
|
||||
s if s == "script" => Ok(Self::default()),
|
||||
s if s == "var" => Ok(Var),
|
||||
s if s == "url" => Ok(Url),
|
||||
_ => Err(L!("Invalid escape style")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags for unescape_string functions.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct UnescapeFlags: u32 {
|
||||
/// escape special fish syntax characters like the semicolon
|
||||
const SPECIAL = 1 << 0;
|
||||
/// allow incomplete escape sequences
|
||||
const INCOMPLETE = 1 << 1;
|
||||
/// don't handle backslash escapes
|
||||
const NO_BACKSLASHES = 1 << 2;
|
||||
}
|
||||
a.windows(b.len()).position(|aw| aw == b)
|
||||
}
|
||||
|
||||
/// This function attempts to distinguish between a console session (at the actual login vty) and a
|
||||
@@ -39,7 +134,7 @@ pub fn subslice_position<T: Eq>(a: &[T], b: &[T]) -> Option<usize> {
|
||||
/// session. We err on the side of assuming it's not a console session. This approach isn't
|
||||
/// bullet-proof and that's OK.
|
||||
pub fn is_console_session() -> bool {
|
||||
static IS_CONSOLE_SESSION: OnceCell<bool> = OnceCell::new();
|
||||
static IS_CONSOLE_SESSION: OnceLock<bool> = OnceLock::new();
|
||||
// TODO(terminal-workaround)
|
||||
*IS_CONSOLE_SESSION.get_or_init(|| {
|
||||
nix::unistd::ttyname(unsafe { std::os::fd::BorrowedFd::borrow_raw(STDIN_FILENO) })
|
||||
@@ -58,3 +153,557 @@ pub fn is_console_session() -> bool {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Exits without invoking destructors (via _exit), useful for code after fork.
|
||||
pub fn exit_without_destructors(code: libc::c_int) -> ! {
|
||||
unsafe { libc::_exit(code) };
|
||||
}
|
||||
|
||||
/// The character to use where the text has been truncated.
|
||||
pub fn get_ellipsis_char() -> char {
|
||||
'\u{2026}' // ('…')
|
||||
}
|
||||
|
||||
/// The character or string to use where text has been truncated (ellipsis if possible, otherwise
|
||||
/// ...)
|
||||
pub fn get_ellipsis_str() -> &'static wstr {
|
||||
L!("\u{2026}")
|
||||
}
|
||||
|
||||
// Only pub for `src/common.rs`
|
||||
pub static OBFUSCATION_READ_CHAR: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub fn get_obfuscation_read_char() -> char {
|
||||
char::from_u32(OBFUSCATION_READ_CHAR.load(Ordering::Relaxed)).unwrap()
|
||||
}
|
||||
|
||||
/// Call read, blocking and repeating on EINTR. Exits on EAGAIN.
|
||||
/// Return the number of bytes read, or 0 on EOF, or an error.
|
||||
pub fn read_blocked(fd: RawFd, buf: &mut [u8]) -> nix::Result<usize> {
|
||||
loop {
|
||||
let res = nix::unistd::read(unsafe { BorrowedFd::borrow_raw(fd) }, buf);
|
||||
if let Err(nix::Error::EINTR) = res {
|
||||
continue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReadExt {
|
||||
/// Like [`std::io::Read::read_to_end`], but does not retry on EINTR.
|
||||
fn read_to_end_interruptible(&mut self, buf: &mut Vec<u8>) -> std::io::Result<()>;
|
||||
}
|
||||
|
||||
impl<T: Read + ?Sized> ReadExt for T {
|
||||
fn read_to_end_interruptible(&mut self, buf: &mut Vec<u8>) -> std::io::Result<()> {
|
||||
let mut chunk = [0_u8; 4096];
|
||||
loop {
|
||||
match self.read(&mut chunk)? {
|
||||
0 => return Ok(()),
|
||||
n => buf.extend_from_slice(&chunk[..n]),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A rusty port of the C++ `write_loop()` function from `common.cpp`. This should be deprecated in
|
||||
/// favor of native rust read/write methods at some point.
|
||||
pub fn safe_write_loop<Fd: AsRawFd>(fd: &Fd, buf: &[u8]) -> std::io::Result<()> {
|
||||
let fd = fd.as_raw_fd();
|
||||
let mut total = 0;
|
||||
while total < buf.len() {
|
||||
match nix::unistd::write(unsafe { BorrowedFd::borrow_raw(fd) }, &buf[total..]) {
|
||||
Ok(written) => {
|
||||
total += written;
|
||||
}
|
||||
Err(err) => {
|
||||
if matches!(err, nix::Error::EAGAIN | nix::Error::EINTR) {
|
||||
continue;
|
||||
}
|
||||
return Err(std::io::Error::from(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub use safe_write_loop as write_loop;
|
||||
|
||||
pub const fn help_section_exists(section: &str) -> bool {
|
||||
let haystack = include_str!("../../../share/help_sections");
|
||||
let needle = section;
|
||||
|
||||
let needle = needle.as_bytes();
|
||||
let haystack = haystack.as_bytes();
|
||||
let nlen = needle.len();
|
||||
let mut line_start = 0;
|
||||
let mut i = 0;
|
||||
while i <= haystack.len() {
|
||||
if i == haystack.len() || haystack[i] == b'\n' {
|
||||
let line_len = i - line_start;
|
||||
|
||||
if line_len == nlen {
|
||||
let mut j = 0;
|
||||
while j < nlen && haystack[line_start + j] == needle[j] {
|
||||
j += 1;
|
||||
}
|
||||
if j == nlen {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
line_start = i + 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! help_section {
|
||||
($section:expr) => {{
|
||||
const {
|
||||
assert!($crate::help_section_exists($section));
|
||||
}
|
||||
|
||||
$section
|
||||
}};
|
||||
}
|
||||
|
||||
pub type Timepoint = f64;
|
||||
|
||||
/// Return the number of seconds from the UNIX epoch, with subsecond precision. This function uses
|
||||
/// the gettimeofday function and will have the same precision as that function.
|
||||
pub fn timef() -> Timepoint {
|
||||
match time::SystemTime::now().duration_since(time::UNIX_EPOCH) {
|
||||
Ok(difference) => difference.as_secs() as f64,
|
||||
Err(until_epoch) => -(until_epoch.duration().as_secs() as f64),
|
||||
}
|
||||
}
|
||||
|
||||
/// Be able to restore the term's foreground process group.
|
||||
/// This is set during startup and not modified after.
|
||||
static INITIAL_FG_PROCESS_GROUP: AtomicI32 = AtomicI32::new(-1); // HACK, should be pid_t
|
||||
const _: () = assert!(mem::size_of::<i32>() >= mem::size_of::<libc::pid_t>());
|
||||
|
||||
/// Save the value of tcgetpgrp so we can restore it on exit.
|
||||
pub fn save_term_foreground_process_group() {
|
||||
INITIAL_FG_PROCESS_GROUP.store(unsafe { libc::tcgetpgrp(STDIN_FILENO) }, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn restore_term_foreground_process_group_for_exit() {
|
||||
// We wish to restore the tty to the initial owner. There's two ways this can go wrong:
|
||||
// 1. We may steal the tty from someone else (#7060).
|
||||
// 2. The call to tcsetpgrp may deliver SIGSTOP to us, and we will not exit.
|
||||
// Hanging on exit seems worse, so ensure that SIGTTOU is ignored so we do not get SIGSTOP.
|
||||
// Note initial_fg_process_group == 0 is possible with Linux pid namespaces.
|
||||
// This is called during shutdown and from a signal handler. We don't bother to complain on
|
||||
// failure because doing so is unlikely to be noticed.
|
||||
// Safety: All of getpgrp, signal, and tcsetpgrp are async-signal-safe.
|
||||
let initial_fg_process_group = INITIAL_FG_PROCESS_GROUP.load(Ordering::Relaxed);
|
||||
if initial_fg_process_group > 0 && initial_fg_process_group != unsafe { libc::getpgrp() } {
|
||||
unsafe {
|
||||
libc::signal(SIGTTOU, SIG_IGN);
|
||||
libc::tcsetpgrp(STDIN_FILENO, initial_fg_process_group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around Cell which supports modifying the contents, scoped to a region of code.
|
||||
/// This provides a somewhat nicer API than ScopedRefCell because you can directly modify the value,
|
||||
/// instead of requiring an accessor function which returns a mutable reference to a field.
|
||||
pub struct ScopedCell<T>(Cell<T>);
|
||||
|
||||
impl<T> Deref for ScopedCell<T> {
|
||||
type Target = Cell<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for ScopedCell<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> ScopedCell<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(Cell::new(value))
|
||||
}
|
||||
|
||||
/// Temporarily modify a value in the ScopedCell, restoring it when the returned object is dropped.
|
||||
///
|
||||
/// This is useful when you want to apply a change for the duration of a scope
|
||||
/// without having to manually restore the previous value.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use fish_common::ScopedCell;
|
||||
///
|
||||
/// let cell = ScopedCell::new(5);
|
||||
/// assert_eq!(cell.get(), 5);
|
||||
///
|
||||
/// {
|
||||
/// let _guard = cell.scoped_mod(|v| *v += 10);
|
||||
/// assert_eq!(cell.get(), 15);
|
||||
/// }
|
||||
///
|
||||
/// // Restored after scope
|
||||
/// assert_eq!(cell.get(), 5);
|
||||
/// ```
|
||||
pub fn scoped_mod<'a, Modifier: FnOnce(&mut T)>(
|
||||
&'a self,
|
||||
modifier: Modifier,
|
||||
) -> impl ScopeGuarding + 'a {
|
||||
let mut val = self.get();
|
||||
modifier(&mut val);
|
||||
let saved = self.replace(val);
|
||||
ScopeGuard::new(self, move |cell| cell.set(saved))
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around RefCell which supports modifying the contents, scoped to a region of code.
|
||||
pub struct ScopedRefCell<T>(RefCell<T>);
|
||||
|
||||
impl<T> Deref for ScopedRefCell<T> {
|
||||
type Target = RefCell<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for ScopedRefCell<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ScopedRefCell<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(RefCell::new(value))
|
||||
}
|
||||
|
||||
/// Temporarily modify a field in the ScopedRefCell, restoring it when the returned guard is dropped.
|
||||
///
|
||||
/// This is useful when you want to change part of a data structure for the duration of a scope,
|
||||
/// and automatically restore the original value afterward.
|
||||
///
|
||||
/// The `accessor` function selects the field to modify by returning a mutable reference to it.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use fish_common::ScopedRefCell;
|
||||
///
|
||||
/// struct State { flag: bool }
|
||||
///
|
||||
/// let cell = ScopedRefCell::new(State { flag: false });
|
||||
/// assert_eq!(cell.borrow().flag, false);
|
||||
///
|
||||
/// {
|
||||
/// let _guard = cell.scoped_set(true, |s| &mut s.flag);
|
||||
/// assert_eq!(cell.borrow().flag, true);
|
||||
/// }
|
||||
///
|
||||
/// // Restored after scope
|
||||
/// assert_eq!(cell.borrow().flag, false);
|
||||
/// ```
|
||||
pub fn scoped_set<'a, Accessor, Value: 'a>(
|
||||
&'a self,
|
||||
value: Value,
|
||||
accessor: Accessor,
|
||||
) -> impl ScopeGuarding + 'a
|
||||
where
|
||||
Accessor: Fn(&mut T) -> &mut Value + 'a,
|
||||
{
|
||||
let mut data = self.borrow_mut();
|
||||
let mut saved = std::mem::replace(accessor(&mut data), value);
|
||||
ScopeGuard::new(self, move |cell| {
|
||||
let mut data = cell.borrow_mut();
|
||||
std::mem::swap((accessor)(&mut data), &mut saved);
|
||||
})
|
||||
}
|
||||
|
||||
/// Convenience method for replacing the entire contents of the ScopedRefCell, restoring it when dropped.
|
||||
///
|
||||
/// Equivalent to `scoped_set(value, |s| s)`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use fish_common::ScopedRefCell;
|
||||
///
|
||||
/// let cell = ScopedRefCell::new(10);
|
||||
/// assert_eq!(*cell.borrow(), 10);
|
||||
///
|
||||
/// {
|
||||
/// let _guard = cell.scoped_replace(99);
|
||||
/// assert_eq!(*cell.borrow(), 99);
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(*cell.borrow(), 10);
|
||||
/// ```
|
||||
pub fn scoped_replace<'a>(&'a self, value: T) -> impl ScopeGuarding + 'a {
|
||||
self.scoped_set(value, |s| s)
|
||||
}
|
||||
}
|
||||
|
||||
/// A RAII cleanup object. Unlike in C++ where there is no borrow checker, we can't just provide a
|
||||
/// callback that modifies live objects willy-nilly because then there would be two &mut references
|
||||
/// to the same object - the original variables we keep around to use and their captured references
|
||||
/// held by the closure until its scope expires.
|
||||
///
|
||||
/// Instead we have a `ScopeGuard` type that takes exclusive ownership of (a mutable reference to)
|
||||
/// the object to be managed. In lieu of keeping the original value around, we obtain a regular or
|
||||
/// mutable reference to it via ScopeGuard's [`Deref`] and [`DerefMut`] impls.
|
||||
///
|
||||
/// The `ScopeGuard` is considered to be the exclusively owner of the passed value for the
|
||||
/// duration of its lifetime. If you need to use the value again, use `ScopeGuard` to shadow the
|
||||
/// value and obtain a reference to it via the `ScopeGuard` itself:
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::prelude::*;
|
||||
/// use fish_common::ScopeGuard;
|
||||
///
|
||||
/// let file = std::fs::File::create("/dev/null").unwrap();
|
||||
/// // Create a scope guard to write to the file when the scope expires.
|
||||
/// // To be able to still use the file, shadow `file` with the ScopeGuard itself.
|
||||
/// let mut file = ScopeGuard::new(file, |mut file| file.write_all(b"goodbye\n").unwrap());
|
||||
/// // Now write to the file normally "through" the capturing ScopeGuard instance.
|
||||
/// file.write_all(b"hello\n").unwrap();
|
||||
///
|
||||
/// // hello will be written first, then goodbye.
|
||||
/// ```
|
||||
pub struct ScopeGuard<T, F: FnOnce(T)>(Option<(T, F)>);
|
||||
|
||||
impl<T, F: FnOnce(T)> ScopeGuard<T, F> {
|
||||
/// Creates a new `ScopeGuard` wrapping `value`. The `on_drop` callback is executed when the
|
||||
/// ScopeGuard's lifetime expires or when it is manually dropped.
|
||||
pub fn new(value: T, on_drop: F) -> Self {
|
||||
Self(Some((value, on_drop)))
|
||||
}
|
||||
|
||||
/// Invokes the callback, consuming the ScopeGuard.
|
||||
pub fn commit(guard: Self) {
|
||||
std::mem::drop(guard);
|
||||
}
|
||||
|
||||
/// Cancels the invocation of the callback, returning the original wrapped value.
|
||||
pub fn cancel(mut guard: Self) -> T {
|
||||
let (value, _) = guard.0.take().expect("Should always have Some value");
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce(T)> Deref for ScopeGuard<T, F> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0.as_ref().unwrap().0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce(T)> DerefMut for ScopeGuard<T, F> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0.as_mut().unwrap().0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce(T)> Drop for ScopeGuard<T, F> {
|
||||
fn drop(&mut self) {
|
||||
if let Some((value, on_drop)) = self.0.take() {
|
||||
on_drop(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait expressing what ScopeGuard can do. This is necessary because our scoped cells return an
|
||||
/// `impl Trait` object and therefore methods on ScopeGuard which take a self parameter cannot be
|
||||
/// used.
|
||||
pub trait ScopeGuarding: DerefMut + Sized {
|
||||
/// Invokes the callback, consuming the guard.
|
||||
fn commit(guard: Self) {
|
||||
std::mem::drop(guard);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce(T)> ScopeGuarding for ScopeGuard<T, F> {}
|
||||
|
||||
pub const fn assert_send<T: Send>() {}
|
||||
pub const fn assert_sync<T: Sync>() {}
|
||||
|
||||
/// Asserts that a slice is alphabetically sorted by a <code>&[wstr]</code> `name` field.
|
||||
///
|
||||
/// Mainly useful for static asserts/const eval.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the given slice is unsorted.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use fish_widestring::{L, wstr};
|
||||
/// use fish_common::assert_sorted_by_name;
|
||||
///
|
||||
/// const COLORS: &[(&wstr, u32)] = &[
|
||||
/// // must be in alphabetical order
|
||||
/// (L!("blue"), 0x0000ff),
|
||||
/// (L!("green"), 0x00ff00),
|
||||
/// (L!("red"), 0xff0000),
|
||||
/// ];
|
||||
///
|
||||
/// assert_sorted_by_name!(COLORS, 0);
|
||||
/// ```
|
||||
///
|
||||
/// While this example would fail to compile:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// use fish_widestring::{L, wstr};
|
||||
/// use fish_common::assert_sorted_by_name;
|
||||
///
|
||||
/// const COLORS: &[(&wstr, u32)] = &[
|
||||
/// // not in alphabetical order
|
||||
/// (L!("green"), 0x00ff00),
|
||||
/// (L!("blue"), 0x0000ff),
|
||||
/// (L!("red"), 0xff0000),
|
||||
/// ];
|
||||
///
|
||||
/// assert_sorted_by_name!(COLORS, 0);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! assert_sorted_by_name {
|
||||
($slice:expr, $field:tt) => {
|
||||
const _: () = {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// ugly const eval workarounds below.
|
||||
const fn cmp_i32(lhs: i32, rhs: i32) -> Ordering {
|
||||
match lhs - rhs {
|
||||
..=-1 => Ordering::Less,
|
||||
0 => Ordering::Equal,
|
||||
1.. => Ordering::Greater,
|
||||
}
|
||||
}
|
||||
|
||||
const fn cmp_slice(s1: &[char], s2: &[char]) -> Ordering {
|
||||
let mut i = 0;
|
||||
while i < s1.len() && i < s2.len() {
|
||||
match cmp_i32(s1[i] as i32, s2[i] as i32) {
|
||||
Ordering::Equal => i += 1,
|
||||
other => return other,
|
||||
}
|
||||
}
|
||||
cmp_i32(s1.len() as i32, s2.len() as i32)
|
||||
}
|
||||
|
||||
let mut i = 1;
|
||||
while i < $slice.len() {
|
||||
let prev = $slice[i - 1].$field.as_char_slice();
|
||||
let cur = $slice[i].$field.as_char_slice();
|
||||
if matches!(cmp_slice(prev, cur), Ordering::Greater) {
|
||||
panic!("array must be sorted");
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
};
|
||||
};
|
||||
($slice:expr) => {
|
||||
assert_sorted_by_name!($slice, name);
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Named {
|
||||
fn name(&self) -> &'static wstr;
|
||||
}
|
||||
|
||||
/// Return a reference to the first entry with the given name, assuming the entries are sorted by
|
||||
/// name. Return None if not found.
|
||||
pub fn get_by_sorted_name<T: Named>(name: &wstr, vals: &'static [T]) -> Option<&'static T> {
|
||||
match vals.binary_search_by_key(&name, |val| val.name()) {
|
||||
Ok(index) => Some(&vals[index]),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an input string, return a prefix of the string up to the first NUL character,
|
||||
/// or the entire string if there is no NUL character.
|
||||
pub fn truncate_at_nul(input: &wstr) -> &wstr {
|
||||
match input.chars().position(|c| c == '\0') {
|
||||
Some(nul_pos) => &input[..nul_pos],
|
||||
None => input,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_scoped_cell() {
|
||||
let cell = ScopedCell::new(42);
|
||||
|
||||
{
|
||||
let _guard = cell.scoped_mod(|x| *x += 1);
|
||||
assert_eq!(cell.get(), 43);
|
||||
}
|
||||
|
||||
assert_eq!(cell.get(), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scoped_refcell() {
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Data {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
let cell = ScopedRefCell::new(Data { x: 1, y: 2 });
|
||||
|
||||
{
|
||||
let _guard = cell.scoped_set(10, |d| &mut d.x);
|
||||
assert_eq!(cell.borrow().x, 10);
|
||||
}
|
||||
assert_eq!(cell.borrow().x, 1);
|
||||
|
||||
{
|
||||
let _guard = cell.scoped_replace(Data { x: 42, y: 99 });
|
||||
assert_eq!(*cell.borrow(), Data { x: 42, y: 99 });
|
||||
}
|
||||
assert_eq!(*cell.borrow(), Data { x: 1, y: 2 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_guard() {
|
||||
let relaxed = std::sync::atomic::Ordering::Relaxed;
|
||||
let counter = std::sync::atomic::AtomicUsize::new(0);
|
||||
{
|
||||
let guard = ScopeGuard::new(123, |arg| {
|
||||
assert_eq!(arg, 123);
|
||||
counter.fetch_add(1, relaxed);
|
||||
});
|
||||
assert_eq!(counter.load(relaxed), 0);
|
||||
std::mem::drop(guard);
|
||||
assert_eq!(counter.load(relaxed), 1);
|
||||
}
|
||||
// commit also invokes the callback.
|
||||
{
|
||||
let guard = ScopeGuard::new(123, |arg| {
|
||||
assert_eq!(arg, 123);
|
||||
counter.fetch_add(1, relaxed);
|
||||
});
|
||||
assert_eq!(counter.load(relaxed), 1);
|
||||
ScopeGuard::commit(guard);
|
||||
assert_eq!(counter.load(relaxed), 2);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_truncate_at_nul() {
|
||||
assert_eq!(truncate_at_nul(L!("abc\0def")), L!("abc"));
|
||||
assert_eq!(truncate_at_nul(L!("abc")), L!("abc"));
|
||||
assert_eq!(truncate_at_nul(L!("\0abc")), L!(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-common.workspace = true
|
||||
fish-wchar.workspace = true
|
||||
fish-widecharwidth.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
libc.workspace = true
|
||||
once_cell.workspace = true
|
||||
widestring.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use fish_build_helper::target_os_is_cygwin;
|
||||
|
||||
fn main() {
|
||||
rsconf::declare_cfg("cygwin", target_os_is_cygwin())
|
||||
rsconf::declare_cfg("cygwin", target_os_is_cygwin());
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
//!
|
||||
//! Many of these functions are more or less broken and incomplete.
|
||||
|
||||
use fish_wchar::prelude::*;
|
||||
use fish_widecharwidth::{WcLookupTable, WcWidth};
|
||||
use once_cell::sync::Lazy;
|
||||
use fish_widestring::prelude::*;
|
||||
use std::cmp;
|
||||
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||
use std::sync::{
|
||||
LazyLock,
|
||||
atomic::{AtomicIsize, Ordering},
|
||||
};
|
||||
|
||||
/// Width of ambiguous East Asian characters and, as of TR11, all private-use characters.
|
||||
/// 1 is the typical default, but we accept any non-negative override via `$fish_ambiguous_width`.
|
||||
@@ -25,7 +27,7 @@
|
||||
// For some reason, this is declared here and exposed here, but is set in `env_dispatch`.
|
||||
pub static FISH_EMOJI_WIDTH: AtomicIsize = AtomicIsize::new(1);
|
||||
|
||||
static WC_LOOKUP_TABLE: Lazy<WcLookupTable> = Lazy::new(WcLookupTable::new);
|
||||
static WC_LOOKUP_TABLE: LazyLock<WcLookupTable> = LazyLock::new(WcLookupTable::new);
|
||||
|
||||
/// A safe wrapper around the system `wcwidth()` function
|
||||
#[cfg(not(cygwin))]
|
||||
@@ -34,7 +36,10 @@ pub fn wcwidth(c: char) -> isize {
|
||||
pub unsafe fn wcwidth(c: libc::wchar_t) -> libc::c_int;
|
||||
}
|
||||
|
||||
const _: () = assert!(std::mem::size_of::<libc::wchar_t>() >= std::mem::size_of::<char>());
|
||||
const {
|
||||
assert!(size_of::<libc::wchar_t>() >= size_of::<char>());
|
||||
}
|
||||
|
||||
let width = unsafe { wcwidth(c as libc::wchar_t) };
|
||||
isize::try_from(width).unwrap()
|
||||
}
|
||||
@@ -183,7 +188,7 @@ pub fn new(mut chars: Chars, to_lowercase: fn(char) -> ToLowercase) -> Self {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::wcscasecmp;
|
||||
use fish_wchar::prelude::*;
|
||||
use fish_widestring::prelude::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extern crate proc_macro;
|
||||
use fish_tempfile::random_filename;
|
||||
use proc_macro::TokenStream;
|
||||
use std::{ffi::OsString, io::Write, path::PathBuf};
|
||||
use std::{ffi::OsString, io::Write as _, path::PathBuf};
|
||||
|
||||
fn unescape_multiline_rust_string(s: String) -> String {
|
||||
if !s.contains('\n') {
|
||||
@@ -26,7 +26,7 @@ enum State {
|
||||
Escaped => match c {
|
||||
'\\' => {
|
||||
unescaped.push('\\');
|
||||
state = Ground
|
||||
state = Ground;
|
||||
}
|
||||
'\n' => state = ContinuationLineLeadingWhitespace,
|
||||
_ => panic!("Unsupported escape sequence '\\{c}' in message string '{s}'"),
|
||||
@@ -35,7 +35,7 @@ enum State {
|
||||
' ' | '\t' => (),
|
||||
_ => {
|
||||
unescaped.push(c);
|
||||
state = Ground
|
||||
state = Ground;
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -47,11 +47,18 @@ enum State {
|
||||
// unsynchronized writers to the same file.
|
||||
fn write_po_entry_to_file(message: &TokenStream, dir: &OsString) {
|
||||
let message_string = unescape_multiline_rust_string(message.to_string());
|
||||
if message_string.contains('\n') {
|
||||
panic!(
|
||||
"Gettext strings may not contain unescaped newlines. Unescaped newline found in '{message_string}'"
|
||||
)
|
||||
}
|
||||
assert!(
|
||||
!message_string.contains('\n'),
|
||||
"Gettext strings may not contain unescaped newlines. Unescaped newline found in '{message_string}'"
|
||||
);
|
||||
let msgid_without_quotes = &message_string[1..(message_string.len() - 1)];
|
||||
// We don't want leading or trailing whitespace in our messages.
|
||||
let trimmed_msgid = msgid_without_quotes.trim();
|
||||
assert_eq!(msgid_without_quotes, trimmed_msgid);
|
||||
assert!(!trimmed_msgid.starts_with("\\n"));
|
||||
assert!(!trimmed_msgid.ends_with("\\n"));
|
||||
assert!(!trimmed_msgid.starts_with("\\t"));
|
||||
assert!(!trimmed_msgid.ends_with("\\t"));
|
||||
// Crude check for format strings. This might result in false positives.
|
||||
let format_string_annotation = if message_string.contains('%') {
|
||||
"#, c-format\n"
|
||||
@@ -90,11 +97,10 @@ pub fn gettext_extract(message: TokenStream) -> TokenStream {
|
||||
let first_token = token_trees
|
||||
.next()
|
||||
.expect("gettext_extract got empty token stream. Expected one token.");
|
||||
if token_trees.next().is_some() {
|
||||
panic!(
|
||||
"Invalid number of tokens passed to gettext_extract. Expected one token, but got more."
|
||||
)
|
||||
}
|
||||
assert!(
|
||||
token_trees.next().is_none(),
|
||||
"Invalid number of tokens passed to gettext_extract. Expected one token, but got more."
|
||||
);
|
||||
let proc_macro2::TokenTree::Group(group) = first_token else {
|
||||
panic!("Expected group in gettext_extract, but got: {first_token:?}");
|
||||
};
|
||||
@@ -102,11 +108,10 @@ pub fn gettext_extract(message: TokenStream) -> TokenStream {
|
||||
let first_group_token = group_tokens
|
||||
.next()
|
||||
.expect("gettext_extract expected one group token but got none.");
|
||||
if group_tokens.next().is_some() {
|
||||
panic!(
|
||||
"Invalid number of tokens in group passed to gettext_extract. Expected one token, but got more."
|
||||
)
|
||||
}
|
||||
assert!(
|
||||
group_tokens.next().is_none(),
|
||||
"Invalid number of tokens in group passed to gettext_extract. Expected one token, but got more."
|
||||
);
|
||||
if let proc_macro2::TokenTree::Literal(_) = first_group_token {
|
||||
write_po_entry_to_file(&message, &dir_path);
|
||||
} else {
|
||||
|
||||
@@ -22,7 +22,7 @@ fn embed_localizations(cache_dir: &Path) {
|
||||
use fish_gettext_mo_file_parser::parse_mo_file;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufWriter, Write},
|
||||
io::{BufWriter, Write as _},
|
||||
};
|
||||
|
||||
let po_dir = fish_build_helper::workspace_root()
|
||||
@@ -91,7 +91,7 @@ fn embed_localizations(cache_dir: &Path) {
|
||||
if cached_map_mtime > po_mtime {
|
||||
// Cached map file is considered up-to-date.
|
||||
continue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the map file.
|
||||
@@ -104,7 +104,7 @@ fn embed_localizations(cache_dir: &Path) {
|
||||
cmd = cmd.arg("--check-format");
|
||||
} else {
|
||||
tmp_mo_file = Some(cache_dir.join("messages.mo"));
|
||||
};
|
||||
}
|
||||
cmd.arg(format!(
|
||||
"--output-file={}",
|
||||
tmp_mo_file
|
||||
@@ -115,12 +115,11 @@ fn embed_localizations(cache_dir: &Path) {
|
||||
.output()
|
||||
.unwrap()
|
||||
};
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"msgfmt failed:\n{}",
|
||||
String::from_utf8(output.stderr).unwrap()
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"msgfmt failed:\n{}",
|
||||
String::from_utf8(output.stderr).unwrap()
|
||||
);
|
||||
let mo_data =
|
||||
tmp_mo_file.map_or(output.stdout, |path| std::fs::read(path).unwrap());
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
const U32_SIZE: usize = std::mem::size_of::<u32>();
|
||||
const U32_SIZE: usize = size_of::<u32>();
|
||||
|
||||
fn read_le_u32(bytes: &[u8]) -> u32 {
|
||||
u32::from_le_bytes(bytes[..U32_SIZE].try_into().unwrap())
|
||||
@@ -47,9 +47,12 @@ fn check_if_revision_is_supported(revision: u32) -> std::io::Result<()> {
|
||||
}
|
||||
|
||||
fn as_usize(value: u32) -> usize {
|
||||
use std::mem::size_of;
|
||||
const _: () = assert!(size_of::<u32>() <= size_of::<usize>());
|
||||
usize::try_from(value).unwrap()
|
||||
const {
|
||||
assert!(size_of::<u32>() <= size_of::<usize>());
|
||||
}
|
||||
|
||||
// SAFETY: `usize` is guaranteed to be at least as wide as `u32` by the const assert above.
|
||||
unsafe { usize::try_from(value).unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn parse_strings(
|
||||
|
||||
@@ -8,7 +8,6 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-gettext-maps.workspace = true
|
||||
once_cell.workspace = true
|
||||
phf.workspace = true
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -1,259 +1,18 @@
|
||||
use fish_gettext_maps::CATALOGS;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{collections::HashSet, sync::Mutex};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{LazyLock, Mutex},
|
||||
};
|
||||
|
||||
type Catalog = &'static phf::Map<&'static str, &'static str>;
|
||||
|
||||
pub struct SetLanguageLints<'a> {
|
||||
pub duplicates: Vec<&'a str>,
|
||||
pub non_existing: Vec<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum LanguagePrecedenceOrigin {
|
||||
Default,
|
||||
LocaleVariable(LocaleVariable),
|
||||
LanguageEnvVar,
|
||||
StatusLanguage,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum LocaleVariable {
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
LANG,
|
||||
#[allow(non_camel_case_types)]
|
||||
LC_MESSAGES,
|
||||
#[allow(non_camel_case_types)]
|
||||
LC_ALL,
|
||||
}
|
||||
|
||||
impl LocaleVariable {
|
||||
fn as_language_precedence_origin(&self) -> LanguagePrecedenceOrigin {
|
||||
LanguagePrecedenceOrigin::LocaleVariable(*self)
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::LANG => "LANG",
|
||||
Self::LC_MESSAGES => "LC_MESSAGES",
|
||||
Self::LC_ALL => "LC_ALL",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LocaleVariable {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalLocalizationState {
|
||||
precedence_origin: LanguagePrecedenceOrigin,
|
||||
language_precedence: Vec<(String, Catalog)>,
|
||||
}
|
||||
|
||||
pub struct PublicLocalizationState {
|
||||
pub precedence_origin: LanguagePrecedenceOrigin,
|
||||
pub language_precedence: Vec<String>,
|
||||
}
|
||||
|
||||
/// Stores the current localization status.
|
||||
/// `is_active` indicates whether localization is currently active, and the reason if it is
|
||||
/// not.
|
||||
/// The `origin` indicates where the values in `language_precedence` were taken from.
|
||||
/// `language_precedence` stores the catalogs in the order they should be used.
|
||||
///
|
||||
/// This struct should be updated when the relevant variables change or `status language` is used
|
||||
/// to modify the localization state.
|
||||
static LOCALIZATION_STATE: Lazy<Mutex<InternalLocalizationState>> =
|
||||
Lazy::new(|| Mutex::new(InternalLocalizationState::new()));
|
||||
|
||||
impl InternalLocalizationState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
precedence_origin: LanguagePrecedenceOrigin::Default,
|
||||
language_precedence: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn to_public(&self) -> PublicLocalizationState {
|
||||
PublicLocalizationState {
|
||||
precedence_origin: self.precedence_origin,
|
||||
language_precedence: self
|
||||
.language_precedence
|
||||
.iter()
|
||||
.map(|(lang, _)| lang.to_owned())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_from_env(
|
||||
&mut self,
|
||||
message_locale: Option<(LocaleVariable, String)>,
|
||||
language_var: Option<Vec<String>>,
|
||||
) {
|
||||
// Do not override values set via `status language`.
|
||||
if self.precedence_origin == LanguagePrecedenceOrigin::StatusLanguage {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((precedence_origin, locale)) = &message_locale {
|
||||
// Regular locale names start with lowercase letters (`ll_CC`, followed by some suffix).
|
||||
// The C or POSIX locale is special, and often used to disable localization.
|
||||
// Their names are upper-case, but variants with suffixes (`C.UTF-8`) exist.
|
||||
// To ensure that such variants are accounted for, we match on prefixes of the
|
||||
// locale name.
|
||||
// https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02
|
||||
fn is_c_locale(locale: &str) -> bool {
|
||||
locale.starts_with('C') || locale.starts_with("POSIX")
|
||||
}
|
||||
if is_c_locale(locale) {
|
||||
self.precedence_origin =
|
||||
LanguagePrecedenceOrigin::LocaleVariable(*precedence_origin);
|
||||
self.language_precedence.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let (precedence_origin, language_list) = if let Some(list) = language_var {
|
||||
(LanguagePrecedenceOrigin::LanguageEnvVar, list)
|
||||
} else if let Some((precedence_origin, locale)) = message_locale {
|
||||
let mut normalized_name = String::new();
|
||||
// Strip off encoding and modifier. (We always expect UTF-8 and don't support modifiers.)
|
||||
for c in locale.chars() {
|
||||
if c.is_alphabetic() || c == '_' {
|
||||
normalized_name.push(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// At this point, the normalized_name should have the shape `ll` or `ll_CC`.
|
||||
(
|
||||
precedence_origin.as_language_precedence_origin(),
|
||||
vec![normalized_name],
|
||||
)
|
||||
} else {
|
||||
(LanguagePrecedenceOrigin::Default, vec![])
|
||||
};
|
||||
|
||||
let mut seen_languages = HashSet::new();
|
||||
self.language_precedence = language_list
|
||||
.into_iter()
|
||||
.flat_map(|lang| find_existing_catalogs(&lang))
|
||||
.filter(|(lang, _)| seen_languages.insert(lang.to_owned()))
|
||||
.collect();
|
||||
self.precedence_origin = precedence_origin;
|
||||
}
|
||||
|
||||
fn update_from_status_language_builtin<'a, 'b: 'a, S: AsRef<str> + 'a>(
|
||||
&mut self,
|
||||
langs: &'b [S],
|
||||
) -> SetLanguageLints<'a> {
|
||||
let mut seen = HashSet::new();
|
||||
let mut duplicates = vec![];
|
||||
for lang in langs {
|
||||
let lang = lang.as_ref();
|
||||
if !seen.insert(lang) {
|
||||
duplicates.push(lang)
|
||||
}
|
||||
}
|
||||
let mut existing_langs = vec![];
|
||||
let mut non_existing = vec![];
|
||||
for lang in langs {
|
||||
let lang = lang.as_ref();
|
||||
if let Some(catalog) = CATALOGS.get(lang) {
|
||||
existing_langs.push((lang.to_owned(), *catalog));
|
||||
} else {
|
||||
non_existing.push(lang);
|
||||
}
|
||||
}
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
let unique_langs = existing_langs
|
||||
.into_iter()
|
||||
.filter(|(lang, _)| seen.insert(lang.to_owned()))
|
||||
.collect();
|
||||
self.language_precedence = unique_langs;
|
||||
self.precedence_origin = LanguagePrecedenceOrigin::StatusLanguage;
|
||||
|
||||
SetLanguageLints {
|
||||
duplicates,
|
||||
non_existing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to find catalogs for `language`.
|
||||
/// `language` must be an ISO 639 language code, optionally followed by an underscore and an ISO
|
||||
/// 3166 country/territory code.
|
||||
/// Uses the catalog with the exact same name as `language` if it exists.
|
||||
/// If a country code is present (`ll_CC`), only the catalog named `ll` will be considered as a fallback.
|
||||
/// If no country code is present (`ll`), all catalogs whose names start with `ll_` will be used in
|
||||
/// arbitrary order.
|
||||
fn find_existing_catalogs(language: &str) -> Vec<(String, Catalog)> {
|
||||
// Try the exact name first.
|
||||
// If there already is a corresponding catalog return the language.
|
||||
if let Some(catalog) = CATALOGS.get(language) {
|
||||
return vec![(language.to_owned(), catalog)];
|
||||
}
|
||||
let language_without_country_code = language.split_once('_').map_or(language, |(ll, _cc)| ll);
|
||||
if language == language_without_country_code {
|
||||
// We have `ll` format. In this case, try to find any catalog whose name starts with `ll_`.
|
||||
// Note that it is important to include the underscore in the pattern, otherwise `ll` might
|
||||
// fall back to `llx_CC`, where `llx` is a 3-letter language identifier.
|
||||
let ll_prefix = format!("{language}_");
|
||||
let mut lang_catalogs = vec![];
|
||||
for (&lang_name, &catalog) in CATALOGS.entries() {
|
||||
if lang_name.starts_with(&ll_prefix) {
|
||||
lang_catalogs.push((lang_name.to_owned(), catalog));
|
||||
}
|
||||
}
|
||||
lang_catalogs
|
||||
} else {
|
||||
// If `language` contained a country code, we only try to fall back to a catalog
|
||||
// without a country code.
|
||||
if let Some(catalog) = CATALOGS.get(language_without_country_code) {
|
||||
vec![(language_without_country_code.to_owned(), catalog)]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_from_env(
|
||||
locale: Option<(LocaleVariable, String)>,
|
||||
language_var: Option<Vec<String>>,
|
||||
) {
|
||||
let mut localization_state = LOCALIZATION_STATE.lock().unwrap();
|
||||
localization_state.update_from_env(locale, language_var);
|
||||
}
|
||||
|
||||
pub fn update_from_status_language_builtin<'a, 'b: 'a, S: AsRef<str> + 'a>(
|
||||
langs: &'b [S],
|
||||
) -> SetLanguageLints<'a> {
|
||||
let mut localization_state = LOCALIZATION_STATE.lock().unwrap();
|
||||
localization_state.update_from_status_language_builtin(langs)
|
||||
}
|
||||
|
||||
pub fn unset_from_status_language_builtin(
|
||||
locale: Option<(LocaleVariable, String)>,
|
||||
language_var: Option<Vec<String>>,
|
||||
) {
|
||||
let mut localization_state = LOCALIZATION_STATE.lock().unwrap();
|
||||
localization_state.precedence_origin = LanguagePrecedenceOrigin::Default;
|
||||
localization_state.update_from_env(locale, language_var);
|
||||
}
|
||||
|
||||
pub fn status_language() -> PublicLocalizationState {
|
||||
let localization_state = LOCALIZATION_STATE.lock().unwrap();
|
||||
localization_state.to_public()
|
||||
}
|
||||
static LANGUAGE_PRECEDENCE: Mutex<Vec<(&'static str, Catalog)>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn gettext(message_str: &'static str) -> Option<&'static str> {
|
||||
let localization_state = LOCALIZATION_STATE.lock().unwrap();
|
||||
let language_precedence = LANGUAGE_PRECEDENCE.lock().unwrap();
|
||||
|
||||
// Use the localization from the highest-precedence language that has one available.
|
||||
for (_, catalog) in localization_state.language_precedence.iter() {
|
||||
for (_, catalog) in language_precedence.iter() {
|
||||
if let Some(localized_str) = catalog.get(message_str) {
|
||||
return Some(localized_str);
|
||||
}
|
||||
@@ -261,8 +20,40 @@ pub fn gettext(message_str: &'static str) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn list_available_languages() -> Vec<&'static str> {
|
||||
let mut langs: Vec<_> = CATALOGS.entries().map(|(&lang, _)| lang).collect();
|
||||
langs.sort();
|
||||
langs
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GettextLocalizationLanguage {
|
||||
language: &'static str,
|
||||
}
|
||||
|
||||
static AVAILABLE_LANGUAGES: LazyLock<HashMap<&'static str, GettextLocalizationLanguage>> =
|
||||
LazyLock::new(|| {
|
||||
HashMap::from_iter(
|
||||
CATALOGS
|
||||
.entries()
|
||||
.map(|(&language, _)| (language, GettextLocalizationLanguage { language })),
|
||||
)
|
||||
});
|
||||
|
||||
pub fn get_available_languages() -> &'static HashMap<&'static str, GettextLocalizationLanguage> {
|
||||
&AVAILABLE_LANGUAGES
|
||||
}
|
||||
|
||||
pub fn set_language_precedence(new_precedence: &[GettextLocalizationLanguage]) {
|
||||
let catalogs = new_precedence
|
||||
.iter()
|
||||
.map(|lang| {
|
||||
(
|
||||
lang.language,
|
||||
*CATALOGS
|
||||
.get(lang.language)
|
||||
.expect("Only languages for which catalogs exist may be passed to gettext."),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
*LANGUAGE_PRECEDENCE.lock().unwrap() = catalogs;
|
||||
}
|
||||
|
||||
pub fn get_language_precedence() -> Vec<&'static str> {
|
||||
let language_precedence = LANGUAGE_PRECEDENCE.lock().unwrap();
|
||||
language_precedence.iter().map(|&(lang, _)| lang).collect()
|
||||
}
|
||||
|
||||
@@ -8,10 +8,11 @@ description = "printf implementation, based on musl"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
assert_matches.workspace = true
|
||||
libc.workspace = true
|
||||
widestring = { workspace = true, optional = true }
|
||||
unicode-segmentation.workspace = true
|
||||
unicode-width.workspace = true
|
||||
widestring = { workspace = true, optional = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -199,27 +199,28 @@ fn to_arg(self) -> Arg<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use assert_matches::assert_matches;
|
||||
#[cfg(feature = "widestring")]
|
||||
use widestring::utf32str;
|
||||
|
||||
#[test]
|
||||
fn test_to_arg() {
|
||||
assert!(matches!("test".to_arg(), Arg::Str("test")));
|
||||
assert!(matches!(String::from("test").to_arg(), Arg::Str(_)));
|
||||
assert_matches!("test".to_arg(), Arg::Str("test"));
|
||||
assert_matches!(String::from("test").to_arg(), Arg::Str(_));
|
||||
#[cfg(feature = "widestring")]
|
||||
assert!(matches!(utf32str!("test").to_arg(), Arg::WStr(_)));
|
||||
assert_matches!(utf32str!("test").to_arg(), Arg::WStr(_));
|
||||
#[cfg(feature = "widestring")]
|
||||
assert!(matches!(WString::from("test").to_arg(), Arg::WStr(_)));
|
||||
assert!(matches!(42f32.to_arg(), Arg::Float(_)));
|
||||
assert!(matches!(42f64.to_arg(), Arg::Float(_)));
|
||||
assert!(matches!('x'.to_arg(), Arg::UInt(120)));
|
||||
assert_matches!(WString::from("test").to_arg(), Arg::WStr(_));
|
||||
assert_matches!(42f32.to_arg(), Arg::Float(_));
|
||||
assert_matches!(42f64.to_arg(), Arg::Float(_));
|
||||
assert_matches!('x'.to_arg(), Arg::UInt(120));
|
||||
let mut usize_val: usize = 0;
|
||||
assert!(matches!((&mut usize_val).to_arg(), Arg::USizeRef(_)));
|
||||
assert!(matches!(42i8.to_arg(), Arg::SInt(42)));
|
||||
assert!(matches!(42i16.to_arg(), Arg::SInt(42)));
|
||||
assert!(matches!(42i32.to_arg(), Arg::SInt(42)));
|
||||
assert!(matches!(42i64.to_arg(), Arg::SInt(42)));
|
||||
assert!(matches!(42isize.to_arg(), Arg::SInt(42)));
|
||||
assert_matches!((&mut usize_val).to_arg(), Arg::USizeRef(_));
|
||||
assert_matches!(42i8.to_arg(), Arg::SInt(42));
|
||||
assert_matches!(42i16.to_arg(), Arg::SInt(42));
|
||||
assert_matches!(42i32.to_arg(), Arg::SInt(42));
|
||||
assert_matches!(42i64.to_arg(), Arg::SInt(42));
|
||||
assert_matches!(42isize.to_arg(), Arg::SInt(42));
|
||||
|
||||
assert_eq!((-42i8).to_arg(), Arg::SInt(-42));
|
||||
assert_eq!((-42i16).to_arg(), Arg::SInt(-42));
|
||||
@@ -227,14 +228,14 @@ fn test_to_arg() {
|
||||
assert_eq!((-42i64).to_arg(), Arg::SInt(-42));
|
||||
assert_eq!((-42isize).to_arg(), Arg::SInt(-42));
|
||||
|
||||
assert!(matches!(42u8.to_arg(), Arg::UInt(42)));
|
||||
assert!(matches!(42u16.to_arg(), Arg::UInt(42)));
|
||||
assert!(matches!(42u32.to_arg(), Arg::UInt(42)));
|
||||
assert!(matches!(42u64.to_arg(), Arg::UInt(42)));
|
||||
assert!(matches!(42usize.to_arg(), Arg::UInt(42)));
|
||||
assert_matches!(42u8.to_arg(), Arg::UInt(42));
|
||||
assert_matches!(42u16.to_arg(), Arg::UInt(42));
|
||||
assert_matches!(42u32.to_arg(), Arg::UInt(42));
|
||||
assert_matches!(42u64.to_arg(), Arg::UInt(42));
|
||||
assert_matches!(42usize.to_arg(), Arg::UInt(42));
|
||||
|
||||
let ptr = std::ptr::from_ref(&42f32);
|
||||
assert!(matches!(ptr.to_arg(), Arg::UInt(_)));
|
||||
assert_matches!(ptr.to_arg(), Arg::UInt(_));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -266,7 +266,7 @@ fn should_round_up(&self, digit_idx: i32, remainder: u32, mod_base: u32) -> bool
|
||||
if rounding_digit & 1 != 0 {
|
||||
round += 2.0;
|
||||
// round now has an odd lsb (though round itself is even).
|
||||
debug_assert!(round.to_bits() & 1 != 0);
|
||||
debug_assert_ne!(round.to_bits() & 1, 0);
|
||||
}
|
||||
|
||||
// Set 'small' to a value which is less than halfway, exactly halfway, or more than halfway
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
use super::locale::Locale;
|
||||
use super::printf_impl::{ConversionSpec, Error, ModifierFlags, pad};
|
||||
use assert_matches::debug_assert_matches;
|
||||
use decimal::{DIGIT_WIDTH, Decimal, DigitLimit};
|
||||
use std::cmp::min;
|
||||
use std::fmt::Write;
|
||||
@@ -129,10 +130,10 @@ pub(crate) fn format_float(
|
||||
) -> Result<usize, Error> {
|
||||
// Only float conversions are expected.
|
||||
type CS = ConversionSpec;
|
||||
debug_assert!(matches!(
|
||||
debug_assert_matches!(
|
||||
conv_spec,
|
||||
CS::e | CS::E | CS::f | CS::F | CS::g | CS::G | CS::a | CS::A
|
||||
));
|
||||
);
|
||||
let prefix = match (y.is_sign_negative(), flags.mark_pos, flags.pad_pos) {
|
||||
(true, _, _) => "-",
|
||||
(false, true, _) => "+",
|
||||
|
||||
@@ -51,7 +51,7 @@ macro_rules! sprintf {
|
||||
{
|
||||
// May be no args!
|
||||
#[allow(unused_imports)]
|
||||
use $crate::ToArg;
|
||||
use $crate::ToArg as _;
|
||||
$crate::printf_c_locale(
|
||||
$target,
|
||||
$fmt,
|
||||
@@ -84,7 +84,7 @@ macro_rules! sprintf {
|
||||
///
|
||||
/// let result = printf_c_locale(&mut output, fmt, &mut args);
|
||||
///
|
||||
/// assert!(result == Ok(10));
|
||||
/// assert_eq!(result, Ok(10));
|
||||
/// assert_eq!(output, "1.2346e+05");
|
||||
/// ```
|
||||
pub fn printf_c_locale(
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
use super::arg::Arg;
|
||||
use super::fmt_fp::format_float;
|
||||
use super::locale::Locale;
|
||||
use assert_matches::assert_matches;
|
||||
use std::fmt::{self, Write};
|
||||
use std::mem;
|
||||
use std::result::Result;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use unicode_segmentation::UnicodeSegmentation as _;
|
||||
use unicode_width::UnicodeWidthStr as _;
|
||||
|
||||
#[cfg(feature = "widestring")]
|
||||
use widestring::Utf32Str as wstr;
|
||||
@@ -57,7 +58,7 @@ fn try_set(&mut self, c: char) -> bool {
|
||||
'+' => self.mark_pos = true,
|
||||
'\'' => self.grouped = true,
|
||||
_ => return false,
|
||||
};
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -301,7 +302,7 @@ pub(super) fn pad(
|
||||
min_width: usize,
|
||||
current_width: usize,
|
||||
) -> fmt::Result {
|
||||
assert!(c == '0' || c == ' ');
|
||||
assert_matches!(c, '0' | ' ');
|
||||
if current_width >= min_width {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -341,7 +342,7 @@ pub(super) fn pad(
|
||||
///
|
||||
/// let result = sprintf_locale(&mut output, fmt, &locale::EN_US_LOCALE, &mut args);
|
||||
///
|
||||
/// assert!(result == Ok(12));
|
||||
/// assert_eq!(result, Ok(12));
|
||||
/// assert_eq!(output, "1,234,567.89");
|
||||
/// ```
|
||||
pub fn sprintf_locale(
|
||||
@@ -371,7 +372,7 @@ pub fn sprintf_locale(
|
||||
}
|
||||
|
||||
// Consume the % at the start of the format specifier.
|
||||
debug_assert!(s.at(0) == Some('%'));
|
||||
debug_assert_eq!(s.at(0), Some('%'));
|
||||
s.advance_by(1);
|
||||
|
||||
// Read modifier flags. '-' and '0' flags are mutually exclusive.
|
||||
@@ -561,7 +562,7 @@ pub fn sprintf_locale(
|
||||
};
|
||||
// Numeric output should be empty iff the value is 0.
|
||||
if spec_is_numeric && body.is_empty() {
|
||||
debug_assert!(arg.as_uint().unwrap() == 0);
|
||||
debug_assert_eq!(arg.as_uint().unwrap(), 0);
|
||||
}
|
||||
|
||||
// Decide if we want to apply thousands grouping to the body, and compute its size.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::arg::ToArg;
|
||||
use crate::locale::{C_LOCALE, EN_US_LOCALE, Locale};
|
||||
use crate::{Error, FormatString, sprintf_locale};
|
||||
use crate::{Error, FormatString as _, sprintf_locale};
|
||||
use std::f64::consts::{E, PI, TAU};
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
@@ -13,7 +13,7 @@ macro_rules! sprintf_check {
|
||||
$(,)? // optional trailing comma
|
||||
) => {
|
||||
{
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use unicode_width::UnicodeWidthStr as _;
|
||||
let mut target = String::new();
|
||||
let mut args = [$($arg.to_arg()),*];
|
||||
let len = $crate::printf_c_locale(
|
||||
@@ -861,8 +861,8 @@ fn test_float_hex_prec() {
|
||||
let mut libc_sprintf = libc_sprintf_one_float_with_precision(&mut c_storage, c"%.*a");
|
||||
|
||||
let mut failed = false;
|
||||
for sign in [1.0, -1.0].into_iter() {
|
||||
for mut v in [0.0, 0.5, 1.0, 1.5, PI, TAU, E].into_iter() {
|
||||
for sign in [1.0, -1.0] {
|
||||
for mut v in [0.0, 0.5, 1.0, 1.5, PI, TAU, E] {
|
||||
v *= sign;
|
||||
for preci in 1..=200_usize {
|
||||
rust_str.clear();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use rand::distr::{Alphanumeric, Distribution};
|
||||
use rand::distr::{Alphanumeric, Distribution as _};
|
||||
|
||||
pub struct TempFile {
|
||||
file: File,
|
||||
@@ -114,7 +114,7 @@ pub fn new_dir() -> std::io::Result<TempDir> {
|
||||
mod tests {
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Seek, Write},
|
||||
io::{Read as _, Seek as _, Write as _},
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
||||
14
crates/util/Cargo.toml
Normal file
14
crates/util/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "fish-util"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.0.0"
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-widestring.workspace = true
|
||||
rand.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Generic utilities library.
|
||||
|
||||
use crate::prelude::*;
|
||||
use rand::{SeedableRng, rngs::SmallRng};
|
||||
use fish_widestring::prelude::*;
|
||||
use rand::{SeedableRng as _, rngs::SmallRng};
|
||||
use std::cmp::Ordering;
|
||||
use std::time;
|
||||
|
||||
@@ -93,7 +93,7 @@ pub fn wcsfilecmp(a: &wstr, b: &wstr) -> Ordering {
|
||||
Ordering::Less // string a is a prefix of b and b is longer
|
||||
}
|
||||
} else {
|
||||
assert!(bi == b.len());
|
||||
assert_eq!(bi, b.len());
|
||||
Ordering::Greater // string b is a prefix of a and a is longer
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ pub fn wcsfilecmp_glob(a: &wstr, b: &wstr) -> Ordering {
|
||||
Ordering::Less // string a is a prefix of b and b is longer
|
||||
}
|
||||
} else {
|
||||
assert!(bi == b.len());
|
||||
assert_eq!(bi, b.len());
|
||||
Ordering::Greater // string b is a prefix of a and a is longer
|
||||
}
|
||||
}
|
||||
@@ -234,33 +234,10 @@ fn wcsfilecmp_leading_digits(a: &wstr, b: &wstr) -> (Ordering, usize, usize) {
|
||||
(ret, ai, bi)
|
||||
}
|
||||
|
||||
/// Finds `needle` in a `haystack` and returns the index of the first matching element, if any.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use fish::util::find_subslice;
|
||||
/// let haystack = b"ABC ABCDAB ABCDABCDABDE";
|
||||
///
|
||||
/// assert_eq!(find_subslice(b"ABCDABD", haystack), Some(15));
|
||||
/// assert_eq!(find_subslice(b"ABCDE", haystack), None);
|
||||
/// ```
|
||||
pub fn find_subslice<T: PartialEq>(
|
||||
needle: impl AsRef<[T]>,
|
||||
haystack: impl AsRef<[T]>,
|
||||
) -> Option<usize> {
|
||||
let needle = needle.as_ref();
|
||||
if needle.is_empty() {
|
||||
return Some(0);
|
||||
}
|
||||
let haystack = haystack.as_ref();
|
||||
haystack.windows(needle.len()).position(|w| w == needle)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::wcsfilecmp;
|
||||
use crate::prelude::*;
|
||||
use fish_widestring::prelude::*;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// Verify the behavior of the `wcsfilecmp()` function.
|
||||
21
crates/wcstringutil/Cargo.toml
Normal file
21
crates/wcstringutil/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "fish-wcstringutil"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.0.0"
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-common.workspace = true
|
||||
fish-fallback.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
|
||||
# Only needed for cygwin detection.
|
||||
# TODO(MSRV>=1.86): remove
|
||||
[build-dependencies]
|
||||
fish-build-helper.workspace = true
|
||||
rsconf.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
3
crates/wcstringutil/build.rs
Normal file
3
crates/wcstringutil/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
rsconf::declare_cfg("cygwin", fish_build_helper::target_os_is_cygwin());
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
//! Helper functions for working with wcstring.
|
||||
|
||||
use crate::common::{get_ellipsis_char, get_ellipsis_str};
|
||||
use crate::prelude::*;
|
||||
use fish_common::{get_ellipsis_char, get_ellipsis_str};
|
||||
use fish_fallback::{fish_wcwidth, lowercase, lowercase_rev, wcscasecmp, wcscasecmp_fuzzy};
|
||||
use fish_wchar::decode_byte_from_char;
|
||||
use fish_widestring::{decode_byte_from_char, prelude::*};
|
||||
|
||||
/// Return the number of newlines in a string.
|
||||
pub fn count_newlines(s: &wstr) -> usize {
|
||||
@@ -19,11 +18,35 @@ pub fn count_newlines(s: &wstr) -> usize {
|
||||
count
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum IsPrefix {
|
||||
Prefix,
|
||||
Equal,
|
||||
}
|
||||
pub fn is_prefix(
|
||||
mut lhs: impl Iterator<Item = char>,
|
||||
mut rhs: impl Iterator<Item = char>,
|
||||
) -> Option<IsPrefix> {
|
||||
use IsPrefix::*;
|
||||
loop {
|
||||
match (lhs.next(), rhs.next()) {
|
||||
(None, None) => return Some(Equal),
|
||||
(None, Some(_)) => return Some(Prefix),
|
||||
(Some(_), None) => return None,
|
||||
(Some(lhs), Some(rhs)) => {
|
||||
if lhs != rhs {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Test if a string prefixes another without regard to case. Returns true if a is a prefix of b.
|
||||
pub fn string_prefixes_string_case_insensitive(proposed_prefix: &wstr, value: &wstr) -> bool {
|
||||
let mut proposed_prefix = lowercase(proposed_prefix.chars());
|
||||
let proposed_prefix = lowercase(proposed_prefix.chars());
|
||||
let value = lowercase(value.chars());
|
||||
proposed_prefix.by_ref().zip(value).all(|(a, b)| a == b) && proposed_prefix.next().is_none()
|
||||
is_prefix(proposed_prefix, value).is_some()
|
||||
}
|
||||
|
||||
pub fn string_prefixes_string_maybe_case_insensitive(
|
||||
@@ -48,9 +71,9 @@ pub fn strip_executable_suffix(path: &wstr) -> Option<&wstr> {
|
||||
|
||||
/// Test if a string is a suffix of another.
|
||||
pub fn string_suffixes_string_case_insensitive(proposed_suffix: &wstr, value: &wstr) -> bool {
|
||||
let mut proposed_suffix = lowercase_rev(proposed_suffix.chars());
|
||||
let proposed_suffix = lowercase_rev(proposed_suffix.chars());
|
||||
let value = lowercase_rev(value.chars());
|
||||
proposed_suffix.by_ref().zip(value).all(|(a, b)| a == b) && proposed_suffix.next().is_none()
|
||||
is_prefix(proposed_suffix, value).is_some()
|
||||
}
|
||||
|
||||
/// Test if a string prefixes another. Returns true if a is a prefix of b.
|
||||
@@ -124,7 +147,7 @@ fn fuzzy_canonicalize(c: char) -> char {
|
||||
// Note that the order of entries below affects the sort order of completions.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ContainType {
|
||||
/// Exact match: `foobar` matches `foo`
|
||||
/// Exact match
|
||||
Exact,
|
||||
/// Prefix match: `foo` matches `foobar`
|
||||
Prefix,
|
||||
@@ -199,7 +222,7 @@ pub fn try_create(
|
||||
// Helper to lazily compute if case insensitive matches should use icase or smartcase.
|
||||
// Use icase if the input contains any uppercase characters, smartcase otherwise.
|
||||
#[inline(always)]
|
||||
fn get_case_fold(s: &widestring::Utf32Str) -> CaseSensitivity {
|
||||
fn get_case_fold(s: &wstr) -> CaseSensitivity {
|
||||
if s.chars().any(|c| c.is_uppercase()) {
|
||||
CaseSensitivity::Insensitive
|
||||
} else {
|
||||
@@ -375,7 +398,7 @@ pub fn split_string_tok<'val>(
|
||||
pos = next_sep + 1;
|
||||
}
|
||||
if pos < end && max_results > 0 {
|
||||
assert!(out.len() + 1 == max_results, "Should have split the max");
|
||||
assert_eq!(out.len() + 1, max_results, "Should have split the max");
|
||||
out.push(wstr::from_char_slice(&val[pos..]));
|
||||
}
|
||||
assert!(out.len() <= max_results, "Got too many results");
|
||||
@@ -557,7 +580,7 @@ mod tests {
|
||||
split_string_tok, string_fuzzy_match_string, string_prefixes_string_case_insensitive,
|
||||
string_suffixes_string_case_insensitive,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use fish_widestring::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_string_prefixes_string_case_insensitive() {
|
||||
@@ -573,6 +596,10 @@ macro_rules! validate {
|
||||
validate!("İ", "i\u{307}_", true);
|
||||
validate!("i\u{307}", "İ", true); // prefix is longer
|
||||
validate!("i", "İ", true);
|
||||
validate!("gs", "gs_", true);
|
||||
validate!("gs_", "gs", false);
|
||||
assert_eq!("İn".to_lowercase().as_str(), "i\u{307}n");
|
||||
validate!("echo in", "echo İnstall", false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -590,6 +617,8 @@ macro_rules! validate {
|
||||
validate!("İ", "i\u{307}", true); // suffix is longer
|
||||
validate!("İ", "_İ", true);
|
||||
validate!("i", "_İ", false);
|
||||
validate!("gs", "_gs", true);
|
||||
validate!("_gs ", "gs", false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
15
crates/wgetopt/Cargo.toml
Normal file
15
crates/wgetopt/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "fish-wgetopt"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.0.0"
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
assert_matches.workspace = true
|
||||
fish-wcstringutil.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -24,7 +24,8 @@
|
||||
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
|
||||
Cambridge, MA 02139, USA. */
|
||||
|
||||
use crate::prelude::*;
|
||||
use assert_matches::assert_matches;
|
||||
use fish_widestring::prelude::*;
|
||||
|
||||
/// Special char used with [`Ordering::ReturnInOrder`].
|
||||
pub const NON_OPTION_CHAR: char = '\x01';
|
||||
@@ -397,7 +398,7 @@ fn update_long_opt(
|
||||
option_index: usize,
|
||||
) -> char {
|
||||
self.wopt_index += 1;
|
||||
assert!(matches!(self.remaining_text.char_at(name_end), '\0' | '='));
|
||||
assert_matches!(self.remaining_text.char_at(name_end), '\0' | '=');
|
||||
|
||||
if self.remaining_text.char_at(name_end) == '=' {
|
||||
if opt_found.arg_type == ArgType::NoArgument {
|
||||
@@ -566,9 +567,9 @@ fn wgetopt_inner(&mut self, longopt_index: &mut usize) -> Option<char> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ArgType, WGetopter, WOption, wopt};
|
||||
use crate::prelude::*;
|
||||
use crate::wcstringutil::join_strings;
|
||||
use super::{ArgType, WGetopter, wopt};
|
||||
use fish_wcstringutil::join_strings;
|
||||
use fish_widestring::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_exchange() {
|
||||
@@ -611,8 +612,8 @@ fn test_exchange() {
|
||||
#[test]
|
||||
fn test_wgetopt() {
|
||||
// Regression test for a crash.
|
||||
const short_options: &wstr = L!("-a");
|
||||
const long_options: &[WOption] = &[wopt(L!("add"), ArgType::NoArgument, 'a')];
|
||||
let short_options = L!("-a");
|
||||
let long_options = &[wopt(L!("add"), ArgType::NoArgument, 'a')];
|
||||
let mut argv = [
|
||||
L!("abbr"),
|
||||
L!("--add"),
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "fish-wchar"
|
||||
name = "fish-widestring"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.0.0"
|
||||
@@ -7,7 +7,6 @@ repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-common.workspace = true
|
||||
widestring.workspace = true
|
||||
|
||||
[lints]
|
||||
@@ -4,23 +4,29 @@
|
||||
//! - wstr: a string slice without a nul terminator. Like `&str` but wide chars.
|
||||
//! - WString: an owning string without a nul terminator. Like `String` but wide chars.
|
||||
|
||||
use fish_common::{ENCODE_DIRECT_BASE, ENCODE_DIRECT_END, subslice_position};
|
||||
pub mod word_char;
|
||||
|
||||
use std::{iter, slice};
|
||||
pub use widestring::{Utf32Str as wstr, Utf32String as WString, utfstr::CharsUtf32};
|
||||
pub use widestring::{Utf32Str as wstr, Utf32String as WString, utf32str as L, utfstr::CharsUtf32};
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{IntoCharIter, L, ToWString, WExt, WString, wstr};
|
||||
}
|
||||
|
||||
/// Creates a wstr string slice, like the "L" prefix of C++.
|
||||
/// The result is of type wstr.
|
||||
/// It is NOT nul-terminated.
|
||||
#[macro_export]
|
||||
macro_rules! L {
|
||||
($string:expr) => {
|
||||
widestring::utf32str!($string)
|
||||
};
|
||||
}
|
||||
// These are in the Unicode private-use range. We really shouldn't use this
|
||||
// range but have little choice in the matter given how our lexer/parser works.
|
||||
// We can't use non-characters for these two ranges because there are only 66 of
|
||||
// them and we need at least 256 + 64.
|
||||
//
|
||||
// If sizeof(wchar_t))==4 we could avoid using private-use chars; however, that
|
||||
// would result in fish having different behavior on machines with 16 versus 32
|
||||
// bit wchar_t. It's better that fish behave the same on both types of systems.
|
||||
//
|
||||
// Note: We don't use the highest 8 bit range (0xF800 - 0xF8FF) because we know
|
||||
// of at least one use of a codepoint in that range: the Apple symbol (0xF8FF)
|
||||
// on Mac OS X. See http://www.unicode.org/faq/private_use.html.
|
||||
pub const ENCODE_DIRECT_BASE: char = '\u{F600}';
|
||||
pub const ENCODE_DIRECT_END: char = char_offset(ENCODE_DIRECT_BASE, 256);
|
||||
|
||||
/// Encode a literal byte in a UTF-32 character. This is required for e.g. the echo builtin, whose
|
||||
/// escape sequences can be used to construct raw byte sequences which are then interpreted as e.g.
|
||||
@@ -47,6 +53,36 @@ pub fn decode_byte_from_char(c: char) -> Option<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn char_offset(base: char, offset: u32) -> char {
|
||||
match char::from_u32(base as u32 + offset) {
|
||||
Some(c) => c,
|
||||
None => panic!("not a valid char"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds `needle` in a `haystack` and returns the index of the first matching element, if any.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use fish_widestring::subslice_position;
|
||||
/// let haystack = b"ABC ABCDAB ABCDABCDABDE";
|
||||
///
|
||||
/// assert_eq!(subslice_position(haystack, b"ABCDABD"), Some(15));
|
||||
/// assert_eq!(subslice_position(haystack, b"ABCDE"), None);
|
||||
/// ```
|
||||
pub fn subslice_position<T: PartialEq>(
|
||||
haystack: impl AsRef<[T]>,
|
||||
needle: impl AsRef<[T]>,
|
||||
) -> Option<usize> {
|
||||
let needle = needle.as_ref();
|
||||
if needle.is_empty() {
|
||||
return Some(0);
|
||||
}
|
||||
let haystack = haystack.as_ref();
|
||||
haystack.windows(needle.len()).position(|w| w == needle)
|
||||
}
|
||||
|
||||
/// Helpers to convert things to widestring.
|
||||
/// This is like std::string::ToString.
|
||||
pub trait ToWString {
|
||||
@@ -204,6 +240,15 @@ fn extend_wstring(&self, out: &mut WString) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: DoubleEndedIterator<Item = char> + Clone, B: DoubleEndedIterator<Item = char> + Clone>
|
||||
IntoCharIter for std::iter::Chain<A, B>
|
||||
{
|
||||
type Iter = std::iter::Chain<A, B>;
|
||||
fn chars(self) -> Self::Iter {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if `prefix` is a prefix of `contents`.
|
||||
fn iter_prefixes_iter<Prefix, Contents>(prefix: Prefix, mut contents: Contents) -> bool
|
||||
where
|
||||
@@ -417,7 +462,7 @@ fn test_prefix() {
|
||||
assert!(L!("abc").starts_with('a'));
|
||||
assert!(L!("abc").starts_with("ab"));
|
||||
assert!(L!("abc").starts_with(L!("ab")));
|
||||
assert!(L!("abc").starts_with(&WString::from_str("abc")));
|
||||
assert!(L!("abc").starts_with(L!("abc")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -427,7 +472,7 @@ fn test_suffix() {
|
||||
assert!(L!("abc").ends_with('c'));
|
||||
assert!(L!("abc").ends_with("bc"));
|
||||
assert!(L!("abc").ends_with(L!("bc")));
|
||||
assert!(L!("abc").ends_with(&WString::from_str("abc")));
|
||||
assert!(L!("abc").ends_with(L!("abc")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
391
crates/widestring/src/word_char.rs
Normal file
391
crates/widestring/src/word_char.rs
Normal file
@@ -0,0 +1,391 @@
|
||||
//! Support for character classification for vi-mode word movements
|
||||
|
||||
use std::{cmp::Ordering, ops::RangeInclusive};
|
||||
|
||||
/// Character class for word movements
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WordCharClass {
|
||||
Blank, // whitespace
|
||||
Newline, // newline
|
||||
Punctuation, // punctuation and symbols
|
||||
Word, // word character
|
||||
Emoji, // emoji
|
||||
Superscript, // superscript (U+2070-U+207F)
|
||||
Subscript, // subscript (U+2080-U+2094)
|
||||
Braille, // braille (U+2800-U+28FF)
|
||||
Hiragana, // Hiragana (U+3040-U+309F)
|
||||
Katakana, // Katakana (U+30A0-U+30FF)
|
||||
Cjk, // CJK Ideographs
|
||||
Hangul, // Hangul Syllables
|
||||
}
|
||||
|
||||
pub fn is_blank(c: char) -> bool {
|
||||
WordCharClass::from_char(c) == WordCharClass::Blank
|
||||
}
|
||||
|
||||
impl WordCharClass {
|
||||
/// Reference: <https://github.com/vim/vim/blob/48940d94/src/mbyte.c#L2866-L2982>
|
||||
pub fn from_char(c: char) -> Self {
|
||||
// Quick check for Latin1 characters
|
||||
if u32::from(c) < 0x100 {
|
||||
// newline
|
||||
if c == '\n' {
|
||||
return WordCharClass::Newline;
|
||||
}
|
||||
// space, tab, NUL, or non-breaking space
|
||||
if matches!(c, ' ' | '\t' | '\0' | '\u{a0}' /* NBSP */) {
|
||||
return WordCharClass::Blank;
|
||||
}
|
||||
if is_latin1_word_char(c) {
|
||||
return WordCharClass::Word;
|
||||
}
|
||||
return WordCharClass::Punctuation;
|
||||
}
|
||||
|
||||
// emoji check
|
||||
if is_emoji(c) {
|
||||
return WordCharClass::Emoji;
|
||||
}
|
||||
|
||||
// binary search in table
|
||||
CLASSES
|
||||
.binary_search_by(|interval| compare_range_to_char(&interval.range, c))
|
||||
.map_or(
|
||||
// most other characters are "word" characters
|
||||
WordCharClass::Word,
|
||||
|i| CLASSES[i].class,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if codepoint is a word character (alphanumeric)
|
||||
/// Note: Different from vim default behavior, we do not regard underscore as a word character!
|
||||
fn is_latin1_word_char(c: char) -> bool {
|
||||
c.is_ascii_alphanumeric() || (matches!( c, | 'À'..='ÿ') && c != '×' && c != '÷')
|
||||
}
|
||||
|
||||
fn compare_range_to_char(range: &RangeInclusive<char>, c: char) -> Ordering {
|
||||
if *range.end() < c {
|
||||
Ordering::Less
|
||||
} else if *range.start() > c {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if codepoint is in emoji table using binary search (like vim's intable)
|
||||
fn is_emoji(c: char) -> bool {
|
||||
EMOJI_ALL
|
||||
.binary_search_by(|range| compare_range_to_char(range, c))
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Character class interval
|
||||
struct ClassInterval {
|
||||
range: RangeInclusive<char>,
|
||||
class: WordCharClass,
|
||||
}
|
||||
|
||||
impl ClassInterval {
|
||||
const fn new(range: RangeInclusive<char>, class: WordCharClass) -> Self {
|
||||
Self { range, class }
|
||||
}
|
||||
|
||||
const fn single(c: char, class: WordCharClass) -> Self {
|
||||
Self::new(c..=c, class)
|
||||
}
|
||||
}
|
||||
|
||||
/// Character classification table (sorted non-overlapping intervals)
|
||||
/// Reference: <https://github.com/vim/vim/blob/48940d94/src/mbyte.c#L2867-L2948>
|
||||
static CLASSES: &[ClassInterval] = {
|
||||
use ClassInterval as I;
|
||||
use WordCharClass::*;
|
||||
&[
|
||||
I::single('\u{037e}', Punctuation), // Greek question mark
|
||||
I::single('\u{0387}', Punctuation), // Greek ano teleia
|
||||
I::new('\u{055a}'..='\u{055f}', Punctuation), // Armenian punctuation
|
||||
I::single('\u{0589}', Punctuation), // Armenian full stop
|
||||
I::single('\u{05be}', Punctuation),
|
||||
I::single('\u{05c0}', Punctuation),
|
||||
I::single('\u{05c3}', Punctuation),
|
||||
I::new('\u{05f3}'..='\u{05f4}', Punctuation),
|
||||
I::single('\u{060c}', Punctuation),
|
||||
I::single('\u{061b}', Punctuation),
|
||||
I::single('\u{061f}', Punctuation),
|
||||
I::new('\u{066a}'..='\u{066d}', Punctuation),
|
||||
I::single('\u{06d4}', Punctuation),
|
||||
I::new('\u{0700}'..='\u{070d}', Punctuation), // Syriac punctuation
|
||||
I::new('\u{0964}'..='\u{0965}', Punctuation),
|
||||
I::single('\u{0970}', Punctuation),
|
||||
I::single('\u{0df4}', Punctuation),
|
||||
I::single('\u{0e4f}', Punctuation),
|
||||
I::new('\u{0e5a}'..='\u{0e5b}', Punctuation),
|
||||
I::new('\u{0f04}'..='\u{0f12}', Punctuation),
|
||||
I::new('\u{0f3a}'..='\u{0f3d}', Punctuation),
|
||||
I::single('\u{0f85}', Punctuation),
|
||||
I::new('\u{104a}'..='\u{104f}', Punctuation), // Myanmar punctuation
|
||||
I::single('\u{10fb}', Punctuation), // Georgian punctuation
|
||||
I::new('\u{1361}'..='\u{1368}', Punctuation), // Ethiopic punctuation
|
||||
I::new('\u{166d}'..='\u{166e}', Punctuation), // Canadian Syl. punctuation
|
||||
I::single('\u{1680}', Blank),
|
||||
I::new('\u{169b}'..='\u{169c}', Punctuation),
|
||||
I::new('\u{16eb}'..='\u{16ed}', Punctuation),
|
||||
I::new('\u{1735}'..='\u{1736}', Punctuation),
|
||||
I::new('\u{17d4}'..='\u{17dc}', Punctuation), // Khmer punctuation
|
||||
I::new('\u{1800}'..='\u{180a}', Punctuation), // Mongolian punctuation
|
||||
I::new('\u{2000}'..='\u{200b}', Blank), // spaces
|
||||
I::new('\u{200c}'..='\u{2027}', Punctuation), // punctuation and symbols
|
||||
I::new('\u{2028}'..='\u{2029}', Blank),
|
||||
I::new('\u{202a}'..='\u{202e}', Punctuation), // punctuation and symbols
|
||||
I::single('\u{202f}', Blank),
|
||||
I::new('\u{2030}'..='\u{205e}', Punctuation), // punctuation and symbols
|
||||
I::single('\u{205f}', Blank),
|
||||
I::new('\u{2060}'..='\u{206f}', Punctuation), // punctuation and symbols
|
||||
I::new('\u{2070}'..='\u{207f}', Superscript),
|
||||
I::new('\u{2080}'..='\u{2094}', Subscript),
|
||||
I::new('\u{20a0}'..='\u{27ff}', Punctuation), // all kinds of symbols
|
||||
I::new('\u{2800}'..='\u{28ff}', Braille),
|
||||
I::new('\u{2900}'..='\u{2998}', Punctuation), // arrows, brackets, etc.
|
||||
I::new('\u{29d8}'..='\u{29db}', Punctuation),
|
||||
I::new('\u{29fc}'..='\u{29fd}', Punctuation),
|
||||
I::new('\u{2e00}'..='\u{2e7f}', Punctuation), // supplemental punctuation
|
||||
I::single('\u{3000}', Blank), // ideographic space
|
||||
I::new('\u{3001}'..='\u{3020}', Punctuation), // ideographic punctuation
|
||||
I::single('\u{3030}', Punctuation),
|
||||
I::single('\u{303d}', Punctuation),
|
||||
I::new('\u{3040}'..='\u{309f}', Hiragana),
|
||||
I::new('\u{30a0}'..='\u{30ff}', Katakana),
|
||||
I::new('\u{3300}'..='\u{9fff}', Cjk),
|
||||
I::new('\u{ac00}'..='\u{d7a3}', Hangul),
|
||||
I::new('\u{f900}'..='\u{faff}', Cjk),
|
||||
I::new('\u{fd3e}'..='\u{fd3f}', Punctuation),
|
||||
I::new('\u{fe30}'..='\u{fe6b}', Punctuation), // punctuation forms
|
||||
I::new('\u{ff00}'..='\u{ff0f}', Punctuation), // half/fullwidth ASCII
|
||||
I::new('\u{ff1a}'..='\u{ff20}', Punctuation), // half/fullwidth ASCII
|
||||
I::new('\u{ff3b}'..='\u{ff40}', Punctuation), // half/fullwidth ASCII
|
||||
I::new('\u{ff5b}'..='\u{ff65}', Punctuation), // half/fullwidth ASCII
|
||||
I::new('\u{1d000}'..='\u{1d24f}', Punctuation), // Musical notation
|
||||
I::new('\u{1d400}'..='\u{1d7ff}', Punctuation), // Mathematical Alphanumeric Symbols
|
||||
I::new('\u{1f000}'..='\u{1f2ff}', Punctuation), // Game pieces; enclosed characters
|
||||
I::new('\u{1f300}'..='\u{1f9ff}', Punctuation), // Many symbol blocks
|
||||
I::new('\u{20000}'..='\u{2a6df}', Cjk),
|
||||
I::new('\u{2a700}'..='\u{2b73f}', Cjk),
|
||||
I::new('\u{2b740}'..='\u{2b81f}', Cjk),
|
||||
I::new('\u{2f800}'..='\u{2fa1f}', Cjk),
|
||||
]
|
||||
};
|
||||
|
||||
/// Reference: <https://github.com/vim/vim/blob/48940d94/src/mbyte.c#L2704-L2852>
|
||||
static EMOJI_ALL: &[RangeInclusive<char>] = &[
|
||||
'\u{203c}'..='\u{203c}',
|
||||
'\u{2049}'..='\u{2049}',
|
||||
'\u{2122}'..='\u{2122}',
|
||||
'\u{2139}'..='\u{2139}',
|
||||
'\u{2194}'..='\u{2199}',
|
||||
'\u{21a9}'..='\u{21aa}',
|
||||
'\u{231a}'..='\u{231b}',
|
||||
'\u{2328}'..='\u{2328}',
|
||||
'\u{23cf}'..='\u{23cf}',
|
||||
'\u{23e9}'..='\u{23f3}',
|
||||
'\u{23f8}'..='\u{23fa}',
|
||||
'\u{24c2}'..='\u{24c2}',
|
||||
'\u{25aa}'..='\u{25ab}',
|
||||
'\u{25b6}'..='\u{25b6}',
|
||||
'\u{25c0}'..='\u{25c0}',
|
||||
'\u{25fb}'..='\u{25fe}',
|
||||
'\u{2600}'..='\u{2604}',
|
||||
'\u{260e}'..='\u{260e}',
|
||||
'\u{2611}'..='\u{2611}',
|
||||
'\u{2614}'..='\u{2615}',
|
||||
'\u{2618}'..='\u{2618}',
|
||||
'\u{261d}'..='\u{261d}',
|
||||
'\u{2620}'..='\u{2620}',
|
||||
'\u{2622}'..='\u{2623}',
|
||||
'\u{2626}'..='\u{2626}',
|
||||
'\u{262a}'..='\u{262a}',
|
||||
'\u{262e}'..='\u{262f}',
|
||||
'\u{2638}'..='\u{263a}',
|
||||
'\u{2640}'..='\u{2640}',
|
||||
'\u{2642}'..='\u{2642}',
|
||||
'\u{2648}'..='\u{2653}',
|
||||
'\u{265f}'..='\u{2660}',
|
||||
'\u{2663}'..='\u{2663}',
|
||||
'\u{2665}'..='\u{2666}',
|
||||
'\u{2668}'..='\u{2668}',
|
||||
'\u{267b}'..='\u{267b}',
|
||||
'\u{267e}'..='\u{267f}',
|
||||
'\u{2692}'..='\u{2697}',
|
||||
'\u{2699}'..='\u{2699}',
|
||||
'\u{269b}'..='\u{269c}',
|
||||
'\u{26a0}'..='\u{26a1}',
|
||||
'\u{26a7}'..='\u{26a7}',
|
||||
'\u{26aa}'..='\u{26ab}',
|
||||
'\u{26b0}'..='\u{26b1}',
|
||||
'\u{26bd}'..='\u{26be}',
|
||||
'\u{26c4}'..='\u{26c5}',
|
||||
'\u{26c8}'..='\u{26c8}',
|
||||
'\u{26ce}'..='\u{26cf}',
|
||||
'\u{26d1}'..='\u{26d1}',
|
||||
'\u{26d3}'..='\u{26d4}',
|
||||
'\u{26e9}'..='\u{26ea}',
|
||||
'\u{26f0}'..='\u{26f5}',
|
||||
'\u{26f7}'..='\u{26fa}',
|
||||
'\u{26fd}'..='\u{26fd}',
|
||||
'\u{2702}'..='\u{2702}',
|
||||
'\u{2705}'..='\u{2705}',
|
||||
'\u{2708}'..='\u{270d}',
|
||||
'\u{270f}'..='\u{270f}',
|
||||
'\u{2712}'..='\u{2712}',
|
||||
'\u{2714}'..='\u{2714}',
|
||||
'\u{2716}'..='\u{2716}',
|
||||
'\u{271d}'..='\u{271d}',
|
||||
'\u{2721}'..='\u{2721}',
|
||||
'\u{2728}'..='\u{2728}',
|
||||
'\u{2733}'..='\u{2734}',
|
||||
'\u{2744}'..='\u{2744}',
|
||||
'\u{2747}'..='\u{2747}',
|
||||
'\u{274c}'..='\u{274c}',
|
||||
'\u{274e}'..='\u{274e}',
|
||||
'\u{2753}'..='\u{2755}',
|
||||
'\u{2757}'..='\u{2757}',
|
||||
'\u{2763}'..='\u{2764}',
|
||||
'\u{2795}'..='\u{2797}',
|
||||
'\u{27a1}'..='\u{27a1}',
|
||||
'\u{27b0}'..='\u{27b0}',
|
||||
'\u{27bf}'..='\u{27bf}',
|
||||
'\u{2934}'..='\u{2935}',
|
||||
'\u{2b05}'..='\u{2b07}',
|
||||
'\u{2b1b}'..='\u{2b1c}',
|
||||
'\u{2b50}'..='\u{2b50}',
|
||||
'\u{2b55}'..='\u{2b55}',
|
||||
'\u{3030}'..='\u{3030}',
|
||||
'\u{303d}'..='\u{303d}',
|
||||
'\u{3297}'..='\u{3297}',
|
||||
'\u{3299}'..='\u{3299}',
|
||||
'\u{1f004}'..='\u{1f004}',
|
||||
'\u{1f0cf}'..='\u{1f0cf}',
|
||||
'\u{1f170}'..='\u{1f171}',
|
||||
'\u{1f17e}'..='\u{1f17f}',
|
||||
'\u{1f18e}'..='\u{1f18e}',
|
||||
'\u{1f191}'..='\u{1f19a}',
|
||||
'\u{1f1e6}'..='\u{1f1ff}',
|
||||
'\u{1f201}'..='\u{1f202}',
|
||||
'\u{1f21a}'..='\u{1f21a}',
|
||||
'\u{1f22f}'..='\u{1f22f}',
|
||||
'\u{1f232}'..='\u{1f23a}',
|
||||
'\u{1f250}'..='\u{1f251}',
|
||||
'\u{1f300}'..='\u{1f321}',
|
||||
'\u{1f324}'..='\u{1f393}',
|
||||
'\u{1f396}'..='\u{1f397}',
|
||||
'\u{1f399}'..='\u{1f39b}',
|
||||
'\u{1f39e}'..='\u{1f3f0}',
|
||||
'\u{1f3f3}'..='\u{1f3f5}',
|
||||
'\u{1f3f7}'..='\u{1f4fd}',
|
||||
'\u{1f4ff}'..='\u{1f53d}',
|
||||
'\u{1f549}'..='\u{1f54e}',
|
||||
'\u{1f550}'..='\u{1f567}',
|
||||
'\u{1f56f}'..='\u{1f570}',
|
||||
'\u{1f573}'..='\u{1f57a}',
|
||||
'\u{1f587}'..='\u{1f587}',
|
||||
'\u{1f58a}'..='\u{1f58d}',
|
||||
'\u{1f590}'..='\u{1f590}',
|
||||
'\u{1f595}'..='\u{1f596}',
|
||||
'\u{1f5a4}'..='\u{1f5a5}',
|
||||
'\u{1f5a8}'..='\u{1f5a8}',
|
||||
'\u{1f5b1}'..='\u{1f5b2}',
|
||||
'\u{1f5bc}'..='\u{1f5bc}',
|
||||
'\u{1f5c2}'..='\u{1f5c4}',
|
||||
'\u{1f5d1}'..='\u{1f5d3}',
|
||||
'\u{1f5dc}'..='\u{1f5de}',
|
||||
'\u{1f5e1}'..='\u{1f5e1}',
|
||||
'\u{1f5e3}'..='\u{1f5e3}',
|
||||
'\u{1f5e8}'..='\u{1f5e8}',
|
||||
'\u{1f5ef}'..='\u{1f5ef}',
|
||||
'\u{1f5f3}'..='\u{1f5f3}',
|
||||
'\u{1f5fa}'..='\u{1f64f}',
|
||||
'\u{1f680}'..='\u{1f6c5}',
|
||||
'\u{1f6cb}'..='\u{1f6d2}',
|
||||
'\u{1f6d5}'..='\u{1f6d7}',
|
||||
'\u{1f6dc}'..='\u{1f6e5}',
|
||||
'\u{1f6e9}'..='\u{1f6e9}',
|
||||
'\u{1f6eb}'..='\u{1f6ec}',
|
||||
'\u{1f6f0}'..='\u{1f6f0}',
|
||||
'\u{1f6f3}'..='\u{1f6fc}',
|
||||
'\u{1f7e0}'..='\u{1f7eb}',
|
||||
'\u{1f7f0}'..='\u{1f7f0}',
|
||||
'\u{1f90c}'..='\u{1f93a}',
|
||||
'\u{1f93c}'..='\u{1f945}',
|
||||
'\u{1f947}'..='\u{1f9ff}',
|
||||
'\u{1fa70}'..='\u{1fa7c}',
|
||||
'\u{1fa80}'..='\u{1fa88}',
|
||||
'\u{1fa90}'..='\u{1fabd}',
|
||||
'\u{1fabf}'..='\u{1fac5}',
|
||||
'\u{1face}'..='\u{1fadb}',
|
||||
'\u{1fae0}'..='\u{1fae8}',
|
||||
'\u{1faf0}'..='\u{1faf8}',
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_blank() {
|
||||
assert_eq!(WordCharClass::from_char(' '), WordCharClass::Blank); // space
|
||||
assert_eq!(WordCharClass::from_char('\t'), WordCharClass::Blank); // tab
|
||||
assert_eq!(WordCharClass::from_char('\0'), WordCharClass::Blank); // NUL
|
||||
assert_eq!(WordCharClass::from_char('\u{a0}'), WordCharClass::Blank); // non-breaking space
|
||||
assert_eq!(WordCharClass::from_char('\u{3000}'), WordCharClass::Blank); // ideographic space
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_word() {
|
||||
assert_eq!(WordCharClass::from_char('a'), WordCharClass::Word); // 'a'
|
||||
assert_eq!(WordCharClass::from_char('Z'), WordCharClass::Word); // 'Z'
|
||||
assert_eq!(WordCharClass::from_char('0'), WordCharClass::Word); // '0'
|
||||
assert_eq!(WordCharClass::from_char('e'), WordCharClass::Word); // 'e' with acute
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_punctuation() {
|
||||
assert_eq!(WordCharClass::from_char('.'), WordCharClass::Punctuation);
|
||||
assert_eq!(WordCharClass::from_char(','), WordCharClass::Punctuation);
|
||||
assert_eq!(WordCharClass::from_char(';'), WordCharClass::Punctuation);
|
||||
assert_eq!(WordCharClass::from_char(','), WordCharClass::Punctuation); // ideographic comma
|
||||
assert_eq!(WordCharClass::from_char('_'), WordCharClass::Punctuation);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cjk() {
|
||||
assert_eq!(WordCharClass::from_char('中'), WordCharClass::Cjk); // CJK character
|
||||
assert_eq!(WordCharClass::from_char('𠀀'), WordCharClass::Cjk); // CJK Extension B
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_japanese() {
|
||||
assert_eq!(WordCharClass::from_char('あ'), WordCharClass::Hiragana);
|
||||
assert_eq!(WordCharClass::from_char('ア'), WordCharClass::Katakana);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hangul() {
|
||||
assert_eq!(WordCharClass::from_char('한'), WordCharClass::Hangul);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emoji() {
|
||||
assert_eq!(WordCharClass::from_char('😀'), WordCharClass::Emoji); // grinning face
|
||||
assert_eq!(WordCharClass::from_char('🚀'), WordCharClass::Emoji); // rocket
|
||||
assert_eq!(WordCharClass::from_char('❤'), WordCharClass::Emoji); // red heart
|
||||
assert_eq!(WordCharClass::from_char('✅'), WordCharClass::Emoji); // check mark
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_special() {
|
||||
assert_eq!(WordCharClass::from_char('⁰'), WordCharClass::Superscript); // superscript zero
|
||||
assert_eq!(WordCharClass::from_char('₀'), WordCharClass::Subscript); // subscript zero
|
||||
assert_eq!(WordCharClass::from_char('\u{2800}'), WordCharClass::Braille); // braille blank
|
||||
}
|
||||
}
|
||||
11
crates/xtask/Cargo.toml
Normal file
11
crates/xtask/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.0.0"
|
||||
rust-version.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap.workspace = true
|
||||
fish-build-helper.workspace = true
|
||||
fish-tempfile.workspace = true
|
||||
28
crates/xtask/src/lib.rs
Normal file
28
crates/xtask/src/lib.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::{ffi::OsStr, process::Command};
|
||||
|
||||
pub trait CommandExt {
|
||||
fn run_or_panic(&mut self);
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
fn run_or_panic(&mut self) {
|
||||
match self.status() {
|
||||
Ok(exit_status) => {
|
||||
if !exit_status.success() {
|
||||
panic!("Command did not run successfully: {:?}", self.get_program())
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to run command: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cargo<I, S>(cargo_args: I)
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
Command::new(env!("CARGO")).args(cargo_args).run_or_panic();
|
||||
}
|
||||
95
crates/xtask/src/main.rs
Normal file
95
crates/xtask/src/main.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
use fish_build_helper::as_os_strs;
|
||||
use std::{path::PathBuf, process::Command};
|
||||
use xtask::{CommandExt as _, cargo};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
name = "cargo xtask",
|
||||
about = "Wrapper for running various utilities",
|
||||
arg_required_else_help(true)
|
||||
)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
task: Task,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Task {
|
||||
/// Run various checks on the repo.
|
||||
Check,
|
||||
/// Build HTML docs
|
||||
HtmlDocs {
|
||||
/// Path to a fish_indent executable. If none is specified, fish_indent will be built.
|
||||
#[arg(long)]
|
||||
fish_indent: Option<PathBuf>,
|
||||
},
|
||||
/// Build man pages
|
||||
ManPages,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
match cli.task {
|
||||
Task::Check => run_checks(),
|
||||
Task::HtmlDocs { fish_indent } => build_html_docs(fish_indent),
|
||||
Task::ManPages => cargo(["build", "--package", "fish-build-man-pages"]),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_checks() {
|
||||
let repo_root_dir = fish_build_helper::workspace_root();
|
||||
let check_script = repo_root_dir.join("build_tools").join("check.sh");
|
||||
Command::new(check_script).run_or_panic();
|
||||
}
|
||||
|
||||
fn build_html_docs(fish_indent: Option<PathBuf>) {
|
||||
let fish_indent_path = fish_indent.unwrap_or_else(|| {
|
||||
// Build fish_indent if no existing one is specified.
|
||||
cargo([
|
||||
"build",
|
||||
"--bin",
|
||||
"fish_indent",
|
||||
"--profile",
|
||||
"dev",
|
||||
"--no-default-features",
|
||||
]);
|
||||
fish_build_helper::fish_build_dir()
|
||||
.join("debug")
|
||||
.join("fish_indent")
|
||||
});
|
||||
// Set path so `sphinx-build` can find `fish_indent`.
|
||||
// Create tempdir to store symlink to fish_indent.
|
||||
// This is done to avoid adding other binaries to the PATH.
|
||||
let tempdir = fish_tempfile::new_dir().unwrap();
|
||||
std::os::unix::fs::symlink(
|
||||
std::fs::canonicalize(fish_indent_path).unwrap(),
|
||||
tempdir.path().join("fish_indent"),
|
||||
)
|
||||
.unwrap();
|
||||
let new_path = format!(
|
||||
"{}:{}",
|
||||
tempdir.path().to_str().unwrap(),
|
||||
fish_build_helper::env_var("PATH").unwrap()
|
||||
);
|
||||
let doc_src_dir = fish_build_helper::workspace_root().join("doc_src");
|
||||
let doctrees_dir = fish_build_helper::fish_doc_dir().join(".doctrees-html");
|
||||
let html_dir = fish_build_helper::fish_doc_dir().join("html");
|
||||
let args = as_os_strs![
|
||||
"-j",
|
||||
"auto",
|
||||
"-q",
|
||||
"-b",
|
||||
"html",
|
||||
"-c",
|
||||
&doc_src_dir,
|
||||
"-d",
|
||||
&doctrees_dir,
|
||||
&doc_src_dir,
|
||||
&html_dir,
|
||||
];
|
||||
Command::new(option_env!("FISH_SPHINX").unwrap_or("sphinx-build"))
|
||||
.env("PATH", new_path)
|
||||
.args(args)
|
||||
.run_or_panic();
|
||||
}
|
||||
@@ -10,7 +10,7 @@ Synopsis
|
||||
[--set-cursor[=MARKER]] ([-f | --function FUNCTION] | EXPANSION)
|
||||
abbr --erase [ [-c | --command COMMAND]... ] NAME ...
|
||||
abbr --rename [ [-c | --command COMMAND]... ] OLD_WORD NEW_WORD
|
||||
abbr --show
|
||||
abbr [--show] [--color WHEN]
|
||||
abbr --list
|
||||
abbr --query NAME ...
|
||||
|
||||
@@ -75,7 +75,6 @@ With **--set-cursor=MARKER**, the cursor is moved to the first occurrence of **M
|
||||
|
||||
With **-f FUNCTION** or **--function FUNCTION**, **FUNCTION** is treated as the name of a fish function instead of a literal replacement. When the abbreviation matches, the function will be called with the matching token as an argument. If the function's exit status is 0 (success), the token will be replaced by the function's output; otherwise the token will be left unchanged. No **EXPANSION** may be given separately.
|
||||
|
||||
|
||||
Examples
|
||||
########
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ Synopsis
|
||||
.. synopsis::
|
||||
|
||||
bind [(-M | --mode) MODE] [(-m | --sets-mode) NEW_MODE] [--preset | --user] [-s | --silent] KEYS COMMAND ...
|
||||
bind [(-M | --mode) MODE] [--preset] [--user] [KEYS]
|
||||
bind [-a | --all] [--preset] [--user]
|
||||
bind [(-M | --mode) MODE] [--preset] [--user] [--color WHEN] [KEYS]
|
||||
bind [-a | --all] [--preset] [--user] [--color WHEN]
|
||||
bind (-f | --function-names)
|
||||
bind (-K | --key-names)
|
||||
bind (-L | --list-modes)
|
||||
@@ -19,8 +19,8 @@ Description
|
||||
``bind`` manages key bindings.
|
||||
|
||||
If both ``KEYS`` and ``COMMAND`` are given, ``bind`` adds (or replaces) a binding in ``MODE``.
|
||||
If only ``KEYS`` is given, any existing binding for those keys in the given ``MODE`` will be printed.
|
||||
If no ``KEYS`` argument is provided, all bindings (in the given ``MODE``) are printed.
|
||||
If only ``KEYS`` is given, ``bind`` lists any existing bindings for those keys in ``MODE`` or in all modes.
|
||||
If no ``KEYS`` argument is provided, ``bind`` lists all bindings in ``MODE`` or in all modes.
|
||||
|
||||
``KEYS`` is a comma-separated list of key names.
|
||||
Modifier keys can be specified by prefixing a key name with a combination of ``ctrl-``, ``alt-``, ``shift-`` and ``super-`` (i.e. the "windows" or "command" key).
|
||||
@@ -102,7 +102,11 @@ The following options are available:
|
||||
**--preset** should only be used in full binding sets (like when working on ``fish_vi_key_bindings``).
|
||||
|
||||
**-s** or **--silent**
|
||||
Silences some of the error messages, including for unknown key names and unbound sequences.
|
||||
Silences error message for unbound sequences.
|
||||
|
||||
**--color** *WHEN*
|
||||
Controls when to use syntax highlighting colors when listing bindings.
|
||||
*WHEN* can be ``auto`` (the default, colorize if the output :doc:`is a terminal <isatty>`), ``always``, or ``never``.
|
||||
|
||||
**-h** or **--help**
|
||||
Displays help about using this command.
|
||||
@@ -127,18 +131,12 @@ The following special input functions are available:
|
||||
move one character to the left, but do not trigger any non-movement-related operations. If the cursor is at the start of
|
||||
the commandline, does nothing. Does not change the selected item in the completion pager UI when shown.
|
||||
|
||||
``backward-bigword``
|
||||
move one whitespace-delimited word to the left
|
||||
|
||||
``backward-token``
|
||||
move one argument to the left
|
||||
|
||||
``backward-delete-char``
|
||||
deletes one character of input to the left of the cursor
|
||||
|
||||
``backward-kill-bigword``
|
||||
move the whitespace-delimited word to the left of the cursor to the killring
|
||||
|
||||
``backward-kill-token``
|
||||
move the argument to the left of the cursor to the killring
|
||||
|
||||
@@ -151,13 +149,25 @@ The following special input functions are available:
|
||||
move one path component to the left of the cursor to the killring. A path component is everything likely to belong to a path component, i.e. not any of the following: `/={,}'\":@ |;<>&`, plus newlines and tabs.
|
||||
|
||||
``backward-kill-word``
|
||||
move the word to the left of the cursor to the killring. The "word" here is everything up to punctuation or whitespace.
|
||||
move the word to the left of the cursor to the killring, until the start of the current word (like vim's ``db``)
|
||||
|
||||
``backward-kill-bigword``
|
||||
move the whitespace-delimited word to the left of the cursor to the killring, until the start of the current word (like vim's ``dB``)
|
||||
|
||||
``backward-path-component``
|
||||
move one :ref:`path component <cmd-bind-backward-kill-path-component>` to the left.
|
||||
move one :ref:`path component <cmd-bind-backward-kill-path-component>` to the left
|
||||
|
||||
``backward-word``
|
||||
move one word to the left
|
||||
move one word to the left, stopping at the start of the previous word (like vim's ``b``, or Emacs' ``M-b`` but differs slightly in word division rules)
|
||||
|
||||
``backward-bigword``
|
||||
move one whitespace-delimited word to the left, stopping at the start of the previous word (like vim's ``B``)
|
||||
|
||||
``backward-word-end``
|
||||
move to the end of the previous word (like vim's ``ge``)
|
||||
|
||||
``backward-bigword-end``
|
||||
move to the end of the previous whitespace-delimited word (like vim's ``gE``)
|
||||
|
||||
``beginning-of-buffer``
|
||||
moves to the beginning of the buffer, i.e. the start of the first line
|
||||
@@ -211,7 +221,8 @@ The following special input functions are available:
|
||||
make the current word lowercase
|
||||
|
||||
``end-of-buffer``
|
||||
moves to the end of the buffer, i.e. the end of the first line
|
||||
moves to the end of the buffer, i.e. the end of the last line;
|
||||
or if already at the end of the commandline, accept the current autosuggestion.
|
||||
|
||||
``end-of-history``
|
||||
move to the end of the history
|
||||
@@ -231,9 +242,6 @@ The following special input functions are available:
|
||||
``exit``
|
||||
exit the shell
|
||||
|
||||
``forward-bigword``
|
||||
move one whitespace-delimited word to the right
|
||||
|
||||
``forward-token``
|
||||
move one argument to the right
|
||||
|
||||
@@ -252,10 +260,29 @@ The following special input functions are available:
|
||||
``forward-single-char``
|
||||
move one character to the right; or if at the end of the commandline, accept a single char from the current autosuggestion.
|
||||
|
||||
.. _cmd-bind-forward-word:
|
||||
|
||||
``forward-word``
|
||||
move one word to the right; or if at the end of the commandline, accept one word
|
||||
move one word to the right, stopping after the end of the current word; or if at the end of the commandline, accept one word
|
||||
from the current autosuggestion.
|
||||
|
||||
``forward-word-vi``
|
||||
like :ref:`forward-word <cmd-bind-forward-word>`, but stops at the start of the next word (like vim's ``w``)
|
||||
|
||||
``forward-word-end``
|
||||
like :ref:`forward-word <cmd-bind-forward-word>`, but stops at the end of the next word (like vim's ``e``)
|
||||
|
||||
.. _cmd-bind-forward-bigword:
|
||||
|
||||
``forward-bigword``
|
||||
move one whitespace-delimited word to the right, stopping after the end of the current word; or if at the end of the commandline, accept one word from the current autosuggestion.
|
||||
|
||||
``forward-bigword-vi``
|
||||
like :ref:`forward-bigword <cmd-bind-forward-bigword>`, but stops at the start of the next word (like vim's ``W``)
|
||||
|
||||
``forward-bigword-end``
|
||||
like :ref:`forward-bigword <cmd-bind-forward-bigword>`, but stops at the end of the next word (like vim's ``E``)
|
||||
|
||||
``history-pager``
|
||||
invoke the searchable pager on history (incremental search); or if the history pager is already active, search further backwards in time.
|
||||
|
||||
@@ -307,9 +334,6 @@ The following special input functions are available:
|
||||
The input function is useful to emulate ``ib`` vi text object.
|
||||
The following brackets are considered: ``([{}])``
|
||||
|
||||
``kill-bigword``
|
||||
move the next whitespace-delimited word to the killring
|
||||
|
||||
``kill-token``
|
||||
move the next argument to the killring
|
||||
|
||||
@@ -329,7 +353,28 @@ The following special input functions are available:
|
||||
move the line (without the following newline) to the killring
|
||||
|
||||
``kill-word``
|
||||
move the next word to the killring
|
||||
move the next word to the killring, stopping after the end of the killed word
|
||||
|
||||
``kill-word-vi``
|
||||
move the next word to the killring, stopping at the start of the next word (like vim's ``dw``)
|
||||
|
||||
``kill-bigword``
|
||||
move the next whitespace-delimited word to the killring, stopping after the end of the current word
|
||||
|
||||
``kill-bigword-vi``
|
||||
move the next whitespace-delimited word to the killring, stopping at the start of the next word (like vim's ``dW``)
|
||||
|
||||
``kill-inner-word``
|
||||
delete the word under the cursor (like vim's ``diw``)
|
||||
|
||||
``kill-inner-bigword``
|
||||
delete the whitespace-delimited word under the cursor (like vim's ``diW``)
|
||||
|
||||
``kill-a-word``
|
||||
delete the word under the cursor plus surrounding whitespace (like vim's ``daw``)
|
||||
|
||||
``kill-a-bigword``
|
||||
delete the whitespace-delimited word under the cursor plus surrounding whitespace (like vim's ``daW``)
|
||||
|
||||
``nextd-or-forward-word``
|
||||
if the commandline is empty, then move forward in the directory history, otherwise move one word to the right;
|
||||
@@ -356,6 +401,13 @@ The following special input functions are available:
|
||||
``self-insert-notfirst``
|
||||
inserts the matching sequence into the command line, unless the cursor is at the beginning
|
||||
|
||||
``get-key``
|
||||
sets :envvar:`fish_key` to the key that was pressed to trigger this binding. Example use::
|
||||
|
||||
for i in (seq 0 9)
|
||||
bind $i get-key 'commandline -i "#$fish_key"' 'set -eg fish_key'
|
||||
end
|
||||
|
||||
``suppress-autosuggestion``
|
||||
remove the current autosuggestion. Returns true if there was a suggestion to remove.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Synopsis
|
||||
|
||||
.. synopsis::
|
||||
|
||||
complete ((-c | --command) | (-p | --path)) COMMAND [OPTIONS]
|
||||
complete ((-c | --command) | (-p | --path)) COMMAND [OPTIONS] [--color WHEN]
|
||||
complete (-C | --do-complete) [--escape] STRING
|
||||
|
||||
Description
|
||||
@@ -74,6 +74,10 @@ The following options are available:
|
||||
**--escape**
|
||||
When used with ``-C``, escape special characters in completions.
|
||||
|
||||
**--color** *WHEN*
|
||||
Controls when to use syntax highlighting colors when printing completions.
|
||||
*WHEN* can be ``auto`` (the default, colorize if the output :doc:`is a terminal <isatty>`), ``always``, or ``never``.
|
||||
|
||||
**-h** or **--help**
|
||||
Displays help about using this command.
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Description
|
||||
|
||||
The ``fish_mode_prompt`` function outputs the mode indicator for use in vi mode.
|
||||
|
||||
The default ``fish_mode_prompt`` function will output indicators about the current vi editor mode displayed to the left of the regular prompt. Define your own function to customize the appearance of the mode indicator. The ``$fish_bind_mode variable`` can be used to determine the current mode. It will be one of ``default``, ``insert``, ``replace_one``, ``replace``, or ``visual``.
|
||||
The default ``fish_mode_prompt`` function will output indicators about the current vi editor mode displayed to the left of the regular prompt. Define your own function to customize the appearance of the mode indicator. The ``$fish_bind_mode`` variable can be used to determine the current mode. It will be one of ``default``, ``insert``, ``replace_one``, ``replace``, ``visual``, or ``operator``.
|
||||
|
||||
You can also define an empty ``fish_mode_prompt`` function to remove the vi mode indicators::
|
||||
|
||||
@@ -55,6 +55,9 @@ Example
|
||||
case visual
|
||||
set_color --bold brmagenta
|
||||
echo 'V'
|
||||
case operator f F t T
|
||||
set_color --bold cyan
|
||||
echo 'N'
|
||||
case '*'
|
||||
set_color --bold red
|
||||
echo '?'
|
||||
|
||||
@@ -24,6 +24,19 @@ The following parameters are available:
|
||||
|
||||
Further information on how to use :ref:`vi mode <vi-mode>`.
|
||||
|
||||
Differences from Vim
|
||||
--------------------
|
||||
|
||||
Fish's vi mode aims to be familiar to vim users, but there are some differences:
|
||||
|
||||
**Word character handling**
|
||||
In vim, underscore (``_``) is treated as a keyword character by default, so word motions like ``w``, ``b``, and ``e`` treat ``foo_bar`` as a single word. In fish, underscore is treated as punctuation, so word motions stop at underscores. For example, pressing ``w`` on ``foo_bar`` in fish stops at the ``_``, while in vim it would jump past the entire identifier.
|
||||
|
||||
**The** ``cw`` **command**
|
||||
In vim, ``cw`` has special behavior: when the cursor is on a non-space character, it behaves like ``ce`` (change to end of word), but when the cursor is on a space, it behaves like ``dwi`` (delete word then insert).
|
||||
|
||||
In fish, ``cw`` always behaves like ``dwi`` - it deletes to the start of the next word (including trailing whitespace), then enters insert mode. To get vim's ``cw`` behavior in fish, use ``ce`` instead.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ Synopsis
|
||||
|
||||
.. synopsis::
|
||||
|
||||
functions [-a | --all] [-n | --names]
|
||||
functions [-D | --details] [-v] FUNCTION
|
||||
functions [-a | --all] [-n | --names] [--color WHEN]
|
||||
functions [-D | --details] [-v] [--color WHEN] FUNCTION
|
||||
functions -c OLDNAME NEWNAME
|
||||
functions -d DESCRIPTION FUNCTION
|
||||
functions [-e | -q] FUNCTION ...
|
||||
@@ -60,6 +60,10 @@ The following options are available:
|
||||
**-t** or **--handlers-type** *TYPE*
|
||||
Show all event handlers matching the given *TYPE*.
|
||||
|
||||
**--color** *WHEN*
|
||||
Controls when to use syntax highlighting colors when printing function definitions.
|
||||
*WHEN* can be ``auto`` (the default, colorize if the output :doc:`is a terminal <isatty>`), ``always``, or ``never``.
|
||||
|
||||
**-h** or **--help**
|
||||
Displays help about using this command.
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ These flags can appear before or immediately after one of the sub-commands liste
|
||||
**-R** or **--reverse**
|
||||
Causes the history search results to be ordered oldest to newest. Which is the order used by most shells. The default is newest to oldest.
|
||||
|
||||
**--color** *WHEN*
|
||||
Controls when to use syntax highlighting colors for the history entries.
|
||||
*WHEN* can be ``auto`` (the default, colorize if the output :doc:`is a terminal <isatty>`), ``always``, or ``never``.
|
||||
|
||||
**-h** or **--help**
|
||||
Displays help for this command.
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ The following options are available:
|
||||
**-r** or **--reverse**
|
||||
Sets reverse mode.
|
||||
|
||||
**-s** or **--strikethrough**
|
||||
Sets strikethrough mode.
|
||||
|
||||
**-u** or **--underline**, or **-uSTYLE** or **--underline=STYLE**
|
||||
Set the underline mode; supported styles are **single** (default), **double**, **curly**, **dotted** and **dashed**.
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ The following options are available:
|
||||
**-q** or **--query**
|
||||
Suppresses all output; this is useful when testing the exit status. For compatibility with old fish versions this is also **--quiet**.
|
||||
|
||||
**--color** *WHEN*
|
||||
Controls when to use syntax highlighting colors when printing function definitions.
|
||||
*WHEN* can be ``auto`` (the default, colorize if the output :doc:`is a terminal <isatty>`), ``always``, or ``never``.
|
||||
|
||||
**-h** or **--help**
|
||||
Displays help about using this command.
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file does only contain a selection of the most common options. For a
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
from glob import glob
|
||||
import os.path
|
||||
@@ -117,17 +117,10 @@ copyright = "fish-shell developers"
|
||||
author = "fish-shell developers"
|
||||
issue_url = "https://github.com/fish-shell/fish-shell/issues"
|
||||
|
||||
# Parsing FISH-BUILD-VERSION-FILE is possible but hard to ensure that it is in the right place
|
||||
# fish_indent is guaranteed to be on PATH for the Pygments highlighter anyway
|
||||
if "FISH_BUILD_VERSION_FILE" in os.environ:
|
||||
f = open(os.environ["FISH_BUILD_VERSION_FILE"], "r")
|
||||
ret = f.readline().strip()
|
||||
elif "FISH_BUILD_VERSION" in os.environ:
|
||||
ret = os.environ["FISH_BUILD_VERSION"]
|
||||
else:
|
||||
ret = subprocess.check_output(
|
||||
("../build_tools/git_version_gen.sh", "--stdout"), stderr=subprocess.STDOUT
|
||||
).decode("utf-8")
|
||||
# From Cargo, or no build system.
|
||||
ret = subprocess.check_output(
|
||||
("../build_tools/git_version_gen.sh"), stderr=subprocess.STDOUT
|
||||
).decode("utf-8")
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = ret.strip().split(" ")[-1]
|
||||
|
||||
@@ -110,6 +110,7 @@ Options accepted by ``set_color`` like
|
||||
``--dim``,
|
||||
``--italics``,
|
||||
``--reverse``,
|
||||
``--strikethrough``,
|
||||
``--underline`` and
|
||||
``--underline-color=``
|
||||
are also accepted.
|
||||
@@ -133,7 +134,7 @@ Variable Meaning
|
||||
.. envvar:: fish_color_end process separators like ``;`` and ``&``
|
||||
.. envvar:: fish_color_error syntax errors
|
||||
.. envvar:: fish_color_param ordinary command parameters
|
||||
.. envvar:: fish_color_valid_path parameters that are filenames (if the file exists)
|
||||
.. envvar:: fish_color_valid_path parameters and redirection targets that are filenames (if the file exists)
|
||||
.. envvar:: fish_color_option options starting with "-", up to the first "--" parameter
|
||||
.. envvar:: fish_color_comment comments like '# important'
|
||||
.. envvar:: fish_color_selection selected text in vi visual mode
|
||||
|
||||
@@ -2044,7 +2044,7 @@ Here is what they mean:
|
||||
- ``remove-percent-self`` turns off the special ``%self`` expansion. It was introduced in 4.0. To get fish's pid, you can use the :envvar:`fish_pid` variable.
|
||||
- ``test-require-arg`` removes :doc:`builtin test <cmds/test>`'s one-argument form (``test "string"``. It was introduced in 4.0. To test if a string is non-empty, use ``test -n "string"``. If disabled, any call to ``test`` that would change sends a :ref:`debug message <debugging-fish>` of category "deprecated-test", so starting fish with ``fish --debug=deprecated-test`` can be used to find offending calls.
|
||||
- ``mark-prompt`` makes fish report to the terminal the beginning and and of both shell prompts and command output.
|
||||
- ``ignore-terminfo`` disables lookup of $TERM in the terminfo database. Use ``no-ignore-terminfo`` to turn it back on.
|
||||
- ``ignore-terminfo`` was introduced in fish 4.1 and cannot be turned off since fish 4.5. It can still be tested for compatibility, but a ``no-ignore-terminfo`` value will be ignored. The flag disabled lookup of $TERM in the terminfo database.
|
||||
- ``query-term`` allows fish to query the terminal by writing escape sequences and reading the terminal's response.
|
||||
This enables features such as :ref:`scrolling <term-compat-cursor-position-report>`.
|
||||
If you use an incompatible terminal, you can -- for the time being -- work around it by running (once) ``set -Ua fish_features no-query-term``.
|
||||
|
||||
@@ -92,7 +92,7 @@ Optional Commands
|
||||
|
||||
* - ``\e[m``
|
||||
- sgr0
|
||||
- Turn off bold/dim/italic/underline/reverse attribute modes and select default colors.
|
||||
- Turn off bold/dim/italic/underline/strikethrough/reverse attribute modes and select default colors.
|
||||
* - ``\e[1m``
|
||||
- bold
|
||||
- Enter bold mode.
|
||||
@@ -120,12 +120,18 @@ Optional Commands
|
||||
* - ``\e[7m``
|
||||
- rev
|
||||
- Enter reverse video mode (swap foreground and background colors).
|
||||
* - ``\e[9m``
|
||||
- smxx
|
||||
- Enter strikethrough mode
|
||||
* - ``\e[23m``
|
||||
- ritm
|
||||
- Exit italic mode.
|
||||
* - ``\e[24m``
|
||||
- rmul
|
||||
- Exit underline mode.
|
||||
* - ``\e[29m``
|
||||
- rmxx
|
||||
- Exit strikethrough mode.
|
||||
* - ``\e[38;5; Ps m``
|
||||
- setaf
|
||||
- Select foreground color Ps from the 256-color-palette.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user