mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-08 08:01:15 -03:00
Compare commits
247 Commits
4.4.0
...
8dbbe71bc6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dbbe71bc6 | ||
|
|
d9d9eced98 | ||
|
|
64a829f0df | ||
|
|
440e7fcbc1 | ||
|
|
52495c8124 | ||
|
|
467b03d715 | ||
|
|
b5c40478f6 | ||
|
|
1286745e78 | ||
|
|
b99ae291d6 | ||
|
|
8ae71c80f4 | ||
|
|
cf6170200c | ||
|
|
c13038b968 | ||
|
|
816077281d | ||
|
|
78ea24a262 | ||
|
|
6a5b9bcde1 | ||
|
|
b7b786aabf | ||
|
|
dc63b7bb20 | ||
|
|
9c819c020e | ||
|
|
dc9b1141c8 | ||
|
|
3d364478ee | ||
|
|
faf331fdad | ||
|
|
47b6c0aec2 | ||
|
|
eb478bfc3e | ||
|
|
63cf79f5f6 | ||
|
|
ff284d642e | ||
|
|
cc64da62a9 | ||
|
|
a974fe990f | ||
|
|
39239724ec | ||
|
|
524a7bac6e | ||
|
|
d649c2aab4 | ||
|
|
30e6aa85e2 | ||
|
|
abd7442521 | ||
|
|
f5c48038b5 | ||
|
|
895a6e7034 | ||
|
|
2193e88423 | ||
|
|
86c3778c2a | ||
|
|
d2653b7cac | ||
|
|
cf16949ce7 | ||
|
|
85311546de | ||
|
|
c44aa32a15 | ||
|
|
65bc9b9e3e | ||
|
|
8125f78a84 | ||
|
|
f0f48b4859 | ||
|
|
a009f87630 | ||
|
|
edb66d4d4e | ||
|
|
f3f675b4cc | ||
|
|
434610494f | ||
|
|
3cce1f3f4c | ||
|
|
a5bde7954e | ||
|
|
99d63c21f1 | ||
|
|
c3e3658157 | ||
|
|
8d92016e72 | ||
|
|
f6a72b4e19 | ||
|
|
2f9c2df10d | ||
|
|
0367aaea7d | ||
|
|
e25b4b6f05 | ||
|
|
90cbfd288e | ||
|
|
68453843d4 | ||
|
|
fb57f95391 | ||
|
|
1ed276292b | ||
|
|
09e46b00cc | ||
|
|
695bc293a9 | ||
|
|
344ff7be88 | ||
|
|
014e3b3aff | ||
|
|
68c7baff90 | ||
|
|
6eaad2cd80 | ||
|
|
b321e38f5a | ||
|
|
a32dd63163 | ||
|
|
ef90afa5b9 | ||
|
|
7bd37dfe55 | ||
|
|
14ce56d2a5 | ||
|
|
01ee6f968d | ||
|
|
7f6dcde5e0 | ||
|
|
34fc573668 | ||
|
|
93cbf2a0e8 | ||
|
|
8194c6eb79 | ||
|
|
3194572156 | ||
|
|
e635816b7f | ||
|
|
2b3ecf22da | ||
|
|
c7ecc3bd78 | ||
|
|
828a20ef30 | ||
|
|
7f5692dfd3 | ||
|
|
454939d5ab | ||
|
|
8756bc3afb | ||
|
|
272f5dda83 | ||
|
|
dde33bab7e | ||
|
|
63f642c9dd | ||
|
|
146384abc6 | ||
|
|
8561008513 | ||
|
|
88d01f7eb8 | ||
|
|
4467822f6e | ||
|
|
5fe1cfb895 | ||
|
|
484032fa9e | ||
|
|
fdd10ba9b2 | ||
|
|
8679464689 | ||
|
|
9ea760c401 | ||
|
|
d36c53c6e2 | ||
|
|
b047450cd0 | ||
|
|
0b93989080 | ||
|
|
6b4ab05cbc | ||
|
|
b5c367f8bf | ||
|
|
a6959aba2a | ||
|
|
0a07e8dbdf | ||
|
|
b2f350d235 | ||
|
|
d1407cfde6 | ||
|
|
f076aa1637 | ||
|
|
96602f5096 | ||
|
|
a36fc0b316 | ||
|
|
d7f413bee9 | ||
|
|
81e9e0bd5c | ||
|
|
5c042575b0 | ||
|
|
a0d8d27f45 | ||
|
|
d1d4cbf3f8 | ||
|
|
7f41c8ba1f | ||
|
|
c8989e1849 | ||
|
|
ae4e258884 | ||
|
|
3f0b4d38ff | ||
|
|
e9379904fb | ||
|
|
116671c577 | ||
|
|
53e1718a68 | ||
|
|
eab84c896e | ||
|
|
ddf99b7063 | ||
|
|
bcda4c5c4d | ||
|
|
1244a47d86 | ||
|
|
4279a4f879 | ||
|
|
efebb7bcdb | ||
|
|
226e818c25 | ||
|
|
888e6d97f9 | ||
|
|
eb7ea0ef9b | ||
|
|
4e41d142fd | ||
|
|
a893dd10f4 | ||
|
|
cba82a3c64 | ||
|
|
6e8c32eb12 | ||
|
|
8ef9864c0c | ||
|
|
786ac339b8 | ||
|
|
dbb6ae6cf5 | ||
|
|
c4aa03a1fd | ||
|
|
e9340a3c43 | ||
|
|
143a53d9c3 | ||
|
|
a282acf083 | ||
|
|
cbd5d7640a | ||
|
|
6f262afe8e | ||
|
|
310eba7156 | ||
|
|
0223edc639 | ||
|
|
6d0bb4a6b8 | ||
|
|
7992fda9fe | ||
|
|
bf5fa4f681 | ||
|
|
735f3ae6ad | ||
|
|
131febed2a | ||
|
|
61dec20abd | ||
|
|
c0bb0d6584 | ||
|
|
c5e4fed021 | ||
|
|
1df7a8ba29 | ||
|
|
121b8fffa6 | ||
|
|
9c4190e40a | ||
|
|
f000149837 | ||
|
|
f6f50df43d | ||
|
|
29160a1592 | ||
|
|
fcdcae72c5 | ||
|
|
971e0b7d37 | ||
|
|
d6ac8a48c0 | ||
|
|
2ba031677f | ||
|
|
c15ea5d1e6 | ||
|
|
78f3c95641 | ||
|
|
149fec8f02 | ||
|
|
1b0fa8f804 | ||
|
|
c38dd1f420 | ||
|
|
b9b32ad157 | ||
|
|
45ac6472e2 | ||
|
|
9235c5de6c | ||
|
|
6c091dbaf4 | ||
|
|
3e7e57945c | ||
|
|
23ce9de1c3 | ||
|
|
6aee7bf378 | ||
|
|
5b360238b2 | ||
|
|
5b44c9668f | ||
|
|
d01a403c65 | ||
|
|
b65649725e | ||
|
|
89c2f1bd6b | ||
|
|
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 |
@@ -21,7 +21,7 @@ indent_size = 4
|
||||
[build_tools/release.sh]
|
||||
max_line_length = 72
|
||||
|
||||
[{Dockerfile,Vagrantfile}]
|
||||
[Vagrantfile]
|
||||
indent_size = 2
|
||||
|
||||
[share/{completions,functions}/**.fish]
|
||||
|
||||
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@v6
|
||||
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
|
||||
|
||||
32
.github/workflows/development-builds.yml
vendored
Normal file
32
.github/workflows/development-builds.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Linux development builds
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- buildscript
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
environment: linux-development
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
- name: Update package database
|
||||
run: sudo apt-get update
|
||||
- name: Install deps
|
||||
run: sudo apt install debhelper devscripts dpkg-dev
|
||||
- name: Create tarball and source packages
|
||||
run: |
|
||||
version=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
mkdir /tmp/gpg
|
||||
echo "$SIGNING_GPG_KEY" > /tmp/gpg/signing-gpg-key
|
||||
mkdir /tmp/fish-built
|
||||
FISH_ARTEFACT_PATH=/tmp/fish-built ./build_tools/make_tarball.sh
|
||||
FISH_ARTEFACT_PATH=/tmp/fish-built DEB_SIGN_KEYFILE=/tmp/gpg/signing-gpg-key ./build_tools/make_linux_packages.sh $version
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: linux-source-packages
|
||||
path: |
|
||||
/tmp/fish-built
|
||||
! /tmp/fish-built/fish-*/* # don't include the unpacked source directory
|
||||
4
.github/workflows/lint-dependencies.yml
vendored
4
.github/workflows/lint-dependencies.yml
vendored
@@ -16,8 +16,8 @@ jobs:
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
|
||||
19
.github/workflows/lint.yml
vendored
19
.github/workflows/lint.yml
vendored
@@ -9,16 +9,16 @@ jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- name: install dependencies
|
||||
run: pip install ruff
|
||||
- name: build fish
|
||||
run: cargo build
|
||||
run: cargo build --bin fish_indent
|
||||
- name: check format
|
||||
run: PATH="target/debug:$PATH" build_tools/style.fish --all --check
|
||||
run: PATH="target/debug:$PATH" cargo xtask format --all --check
|
||||
- name: check rustfmt
|
||||
run: find build.rs crates src -type f -name '*.rs' | xargs rustfmt --check
|
||||
|
||||
@@ -35,22 +35,31 @@ jobs:
|
||||
- rust_version: "msrv"
|
||||
features: ""
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
- name: Patch Cargo.toml to deny unknown lints
|
||||
run: |
|
||||
if [ "${{ matrix.rust_version }}" = stable ]; then
|
||||
sed -i /^rust.unknown_lints/d Cargo.toml
|
||||
fi
|
||||
- name: cargo clippy
|
||||
run: cargo clippy --workspace --all-targets ${{ matrix.features }} -- --deny=warnings
|
||||
|
||||
rustdoc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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@v6
|
||||
- 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@v6
|
||||
- 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@v6
|
||||
- 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@v6
|
||||
- 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@v6
|
||||
- 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 }}
|
||||
|
||||
26
.github/workflows/test.yml
vendored
26
.github/workflows/test.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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@v6
|
||||
- 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@v6
|
||||
- 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@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, build_tools/update-dependencies.sh
|
||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
- name: Install deps
|
||||
run: |
|
||||
@@ -155,15 +158,22 @@ jobs:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
id: msys2
|
||||
- name: Install deps
|
||||
# Not using setup-msys2 `install` option to make it easier to copy/paste
|
||||
run: |
|
||||
pacman --noconfirm -S --needed git rust
|
||||
- name: rebase
|
||||
env:
|
||||
MSYS2_LOCATION: ${{ steps.msys2.outputs.msys2-location }}
|
||||
shell: cmd
|
||||
run: |
|
||||
"%MSYS2_LOCATION%\usr\bin\dash" /usr/bin/rebaseall -p -v
|
||||
- name: cargo build
|
||||
run: |
|
||||
cargo build
|
||||
|
||||
@@ -1,3 +1,94 @@
|
||||
fish ?.?.? (released ???)
|
||||
=========================
|
||||
|
||||
Notable improvements and fixes
|
||||
------------------------------
|
||||
|
||||
Deprecations and removed features
|
||||
---------------------------------
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
|
||||
Improved terminal support
|
||||
-------------------------
|
||||
|
||||
Other improvements
|
||||
------------------
|
||||
- History is no longer corrupted with NUL bytes when fish receives SIGTERM or SIGHUP (:issue:`10300`).
|
||||
- ``fish_update_completions`` now handles groff ``\X'...'`` device control escapes, fixing completion generation for man pages produced by help2man 1.50 and later (such as coreutils 9.10).
|
||||
|
||||
For distributors and developers
|
||||
-------------------------------
|
||||
- When the default global config directory (``$PREFIX/etc/fish``) exists but has been overridden with ``-DCMAKE_INSTALL_SYSCONFDIR``, fish will now respect that override (:issue:`10748`).
|
||||
|
||||
Regression fixes:
|
||||
-----------------
|
||||
|
||||
fish 4.6.0 (released March 28, 2026)
|
||||
====================================
|
||||
|
||||
Notable improvements and fixes
|
||||
------------------------------
|
||||
- New Spanish translations (:issue:`12489`).
|
||||
- New Japanese translations (:issue:`12499`).
|
||||
|
||||
Deprecations and removed features
|
||||
---------------------------------
|
||||
- The default width for emoji is switched from 1 to 2, improving the experience for users connecting to old systems from modern desktops. Users of old desktops who notice that lines containing emoji are misaligned can set ``$fish_emoji_width`` back to 1 (:issue:`12562`).
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
- The tab completion pager now left-justifies the description of each column (:issue:`12546`).
|
||||
- fish now supports the ``SHELL_PROMPT_PREFIX``, ``SHELL_PROMPT_SUFFIX``, and ``SHELL_WELCOME`` environment variables. The prefix and suffix are automatically prepended and appended to the left prompt, and the welcome message is displayed on startup after the greeting.
|
||||
These variables are set by systemd's ``run0`` for example (:issue:`10924`).
|
||||
|
||||
Improved terminal support
|
||||
-------------------------
|
||||
- ``set_color`` is able to turn off italics, reverse mode, strikethrough and underline individually (e.g. ``--italics=off``).
|
||||
- ``set_color`` learned the foreground (``--foreground`` or ``-f``) and reset (``--reset``) options.
|
||||
- An error caused by slow terminal responses at macOS startup has been addressed (:issue:`12571`).
|
||||
|
||||
Other improvements
|
||||
------------------
|
||||
- Signals like ``SIGWINCH`` (as sent on terminal resize) no longer interrupt builtin output (:issue:`12496`).
|
||||
- For compatibility with Bash, fish now accepts ``|&`` as alternate spelling of ``&|``, for piping both standard output and standard error (:issue:`11516`).
|
||||
- ``fish_indent`` now preserves comments and newlines immediately preceding a brace block (``{ }``) (:issue:`12505`).
|
||||
- A crash when suspending certain pipelines with :kbd:`ctrl-z` has been fixed (:issue:`12301`).
|
||||
|
||||
For distributors and developers
|
||||
-------------------------------
|
||||
- ``cargo xtask`` subcommands no longer panic on test failures.
|
||||
|
||||
Regression fixes:
|
||||
-----------------
|
||||
- (from 4.5.0) Intermediate ``⏎`` artifact when redrawing prompt (:issue:`12476`).
|
||||
- (from 4.4.0) ``history`` honors explicitly specified ``--color=`` again (:issue:`12512`).
|
||||
- (from 4.4.0) Vi mode ``dl`` and ``dh`` (:issue:`12461`).
|
||||
- (from 4.3.0) Error completing of commands starting with ``-`` (:issue:`12522`).
|
||||
|
||||
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)
|
||||
=======================================
|
||||
|
||||
@@ -15,9 +106,9 @@ Interactive improvements
|
||||
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``.
|
||||
- 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.
|
||||
- New ``catppuccin-*`` color themes.
|
||||
|
||||
Improved terminal support
|
||||
-------------------------
|
||||
|
||||
@@ -53,36 +53,36 @@ add_definitions(-DCMAKE_SOURCE_DIR="${REAL_CMAKE_SOURCE_DIR}")
|
||||
|
||||
set(build_types Release RelWithDebInfo Debug "")
|
||||
if(NOT "${CMAKE_BUILD_TYPE}" IN_LIST build_types)
|
||||
message(WARNING "Unsupported build type ${CMAKE_BUILD_TYPE}. If this doesn't build, try one of Release, RelWithDebInfo or Debug")
|
||||
message(WARNING "Unsupported build type ${CMAKE_BUILD_TYPE}. If this doesn't build, try one of Release, RelWithDebInfo or Debug")
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
fish ALL
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E
|
||||
env ${VARS_FOR_CARGO}
|
||||
${Rust_CARGO}
|
||||
build --bin fish
|
||||
$<$<CONFIG:Release>:--release>
|
||||
$<$<CONFIG:RelWithDebInfo>:--profile=release-with-debug>
|
||||
--target ${Rust_CARGO_TARGET}
|
||||
--no-default-features
|
||||
--features=${FISH_CARGO_FEATURES}
|
||||
${CARGO_FLAGS}
|
||||
&&
|
||||
"${CMAKE_COMMAND}" -E
|
||||
copy "${rust_target_dir}/${rust_profile}/fish" "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
USES_TERMINAL
|
||||
fish ALL
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" -E
|
||||
env ${VARS_FOR_CARGO}
|
||||
${Rust_CARGO}
|
||||
build --bin fish
|
||||
$<$<CONFIG:Release>:--release>
|
||||
$<$<CONFIG:RelWithDebInfo>:--profile=release-with-debug>
|
||||
--target ${Rust_CARGO_TARGET}
|
||||
--no-default-features
|
||||
--features=${FISH_CARGO_FEATURES}
|
||||
${CARGO_FLAGS}
|
||||
&&
|
||||
"${CMAKE_COMMAND}" -E
|
||||
copy "${rust_target_dir}/${rust_profile}/fish" "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
function(CREATE_LINK target)
|
||||
add_custom_target(
|
||||
${target} ALL
|
||||
DEPENDS fish
|
||||
COMMAND ln -f fish ${target}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
add_custom_target(
|
||||
${target} ALL
|
||||
DEPENDS fish
|
||||
COMMAND ln -f fish ${target}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
endfunction(CREATE_LINK)
|
||||
|
||||
# Define fish_indent.
|
||||
|
||||
@@ -133,12 +133,12 @@ For formatting, we use:
|
||||
- ``fish_indent`` (shipped with fish) for fish script
|
||||
- ``ruff format`` for Python
|
||||
|
||||
To reformat files, there is a script
|
||||
To reformat files, there is an xtask
|
||||
|
||||
::
|
||||
|
||||
build_tools/style.fish --all
|
||||
build_tools/style.fish somefile.rs some.fish
|
||||
cargo xtask format --all
|
||||
cargo xtask format somefile.rs some.fish
|
||||
|
||||
Fish Script Style Guide
|
||||
-----------------------
|
||||
|
||||
139
Cargo.lock
generated
139
Cargo.lock
generated
@@ -260,7 +260,7 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "4.4.0"
|
||||
version = "4.6.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"bitflags",
|
||||
@@ -272,6 +272,7 @@ dependencies = [
|
||||
"fish-color",
|
||||
"fish-common",
|
||||
"fish-fallback",
|
||||
"fish-feature-flags",
|
||||
"fish-gettext",
|
||||
"fish-gettext-extraction",
|
||||
"fish-gettext-mo-file-parser",
|
||||
@@ -290,13 +291,12 @@ dependencies = [
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"pcre2",
|
||||
"phf_codegen 0.13.1",
|
||||
"phf_codegen",
|
||||
"portable-atomic",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
"rsconf",
|
||||
"rust-embed",
|
||||
"serial_test",
|
||||
"terminfo",
|
||||
"unix_path",
|
||||
"xterm-color",
|
||||
]
|
||||
@@ -329,9 +329,12 @@ name = "fish-common"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fish-build-helper",
|
||||
"fish-feature-flags",
|
||||
"fish-widestring",
|
||||
"libc",
|
||||
"nix",
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -347,12 +350,19 @@ dependencies = [
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-feature-flags"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-gettext"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-gettext-maps",
|
||||
"phf 0.13.1",
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -370,8 +380,8 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-build-helper",
|
||||
"fish-gettext-mo-file-parser",
|
||||
"phf 0.13.1",
|
||||
"phf_codegen 0.13.1",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"rsconf",
|
||||
]
|
||||
|
||||
@@ -395,15 +405,18 @@ name = "fish-tempfile"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"rand 0.9.2",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fish-util"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"fish-widestring",
|
||||
"rand 0.9.2",
|
||||
"libc",
|
||||
"nix",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -411,7 +424,6 @@ name = "fish-wcstringutil"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"fish-build-helper",
|
||||
"fish-common",
|
||||
"fish-fallback",
|
||||
"fish-widestring",
|
||||
"rsconf",
|
||||
@@ -434,15 +446,11 @@ version = "0.0.0"
|
||||
name = "fish-widestring"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"unicode-width",
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
@@ -599,12 +607,6 @@ 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.31.1"
|
||||
@@ -617,16 +619,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"
|
||||
@@ -703,32 +695,13 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
|
||||
dependencies = [
|
||||
"phf_shared 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.3",
|
||||
"phf_shared 0.11.3",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -737,18 +710,8 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
|
||||
dependencies = [
|
||||
"phf_generator 0.13.1",
|
||||
"phf_shared 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared 0.11.3",
|
||||
"rand 0.8.5",
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -758,16 +721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"phf_shared 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -824,15 +778,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"
|
||||
@@ -840,7 +785,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.5",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -850,15 +795,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.5",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
@@ -1045,9 +984,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shellexpand"
|
||||
version = "3.1.1"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb"
|
||||
checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
]
|
||||
@@ -1087,18 +1026,6 @@ dependencies = [
|
||||
"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.18"
|
||||
@@ -1235,9 +1162,11 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
name = "xtask"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap",
|
||||
"fish-build-helper",
|
||||
"fish-tempfile",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
27
Cargo.toml
27
Cargo.toml
@@ -11,6 +11,7 @@ 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]
|
||||
anstyle = "1.0.13"
|
||||
assert_matches = "1.5.0"
|
||||
bitflags = "2.5.0"
|
||||
cc = "1.0.94"
|
||||
@@ -22,6 +23,7 @@ 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-feature-flags = { path = "crates/feature-flags" }
|
||||
fish-gettext = { path = "crates/gettext" }
|
||||
fish-gettext-extraction = { path = "crates/gettext-extraction" }
|
||||
fish-gettext-maps = { path = "crates/gettext-maps" }
|
||||
@@ -39,7 +41,17 @@ libc = "0.2.177"
|
||||
# disabling default features uses the stdlib instead, but it doubles the time to rewrite the history
|
||||
# files as of 22 April 2024.
|
||||
lru = "0.16.2"
|
||||
nix = { version = "0.31.1", default-features = false, features = ["event", "fs", "inotify", "hostname", "resource", "process", "signal", "term", "user"] }
|
||||
nix = { version = "0.31.1", default-features = false, features = [
|
||||
"event",
|
||||
"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 = [
|
||||
@@ -62,12 +74,11 @@ rust-embed = { version = "8.11.0", features = [
|
||||
"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"
|
||||
unix_path = "1.0.1"
|
||||
walkdir = "2.5.0"
|
||||
xterm-color = "1.0.1"
|
||||
|
||||
[profile.release]
|
||||
@@ -80,7 +91,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.4.0"
|
||||
version = "4.6.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
default-run = "fish"
|
||||
@@ -98,6 +109,7 @@ fish-build-man-pages = { workspace = true, optional = true }
|
||||
fish-color.workspace = true
|
||||
fish-common.workspace = true
|
||||
fish-fallback.workspace = true
|
||||
fish-feature-flags.workspace = true
|
||||
fish-gettext = { workspace = true, optional = true }
|
||||
fish-gettext-extraction = { workspace = true, optional = true }
|
||||
fish-printf.workspace = true
|
||||
@@ -116,7 +128,6 @@ num-traits.workspace = true
|
||||
once_cell.workspace = true
|
||||
pcre2.workspace = true
|
||||
rand.workspace = true
|
||||
terminfo.workspace = true
|
||||
xterm-color.workspace = true
|
||||
|
||||
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
|
||||
@@ -184,7 +195,7 @@ tsan = []
|
||||
[workspace.lints]
|
||||
rust.non_camel_case_types = "allow"
|
||||
rust.non_upper_case_globals = "allow"
|
||||
rust.unknown_lints = "allow"
|
||||
rust.unknown_lints = { level = "allow", priority = -1 }
|
||||
rust.unstable_name_collisions = "allow"
|
||||
rustdoc.private_intra_doc_links = "allow"
|
||||
|
||||
@@ -194,7 +205,7 @@ 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
|
||||
len_without_is_empty = "allow" # we're not a library crate
|
||||
let_and_return = "allow"
|
||||
manual_assert = "warn"
|
||||
manual_range_contains = "allow"
|
||||
@@ -207,7 +218,9 @@ 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.
|
||||
|
||||
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
|
||||
8
build.rs
8
build.rs
@@ -159,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()),
|
||||
))
|
||||
});
|
||||
|
||||
@@ -199,5 +197,5 @@ fn get_version() -> String {
|
||||
)
|
||||
.unwrap()
|
||||
.trim_ascii_end()
|
||||
.to_string()
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
@@ -8,11 +8,29 @@ if [ "$FISH_CHECK_LINT" = false ]; then
|
||||
lint=false
|
||||
fi
|
||||
|
||||
case "$(uname)" in
|
||||
MSYS*)
|
||||
is_cygwin=true
|
||||
cygwin_var=MSYS
|
||||
;;
|
||||
CYGWIN*)
|
||||
is_cygwin=true
|
||||
cygwin_var=CYGWIN
|
||||
;;
|
||||
*)
|
||||
is_cygwin=false
|
||||
;;
|
||||
esac
|
||||
|
||||
check_dependency_versions=false
|
||||
if [ "${FISH_CHECK_DEPENDENCY_VERSIONS:-false}" != false ]; then
|
||||
check_dependency_versions=true
|
||||
fi
|
||||
|
||||
green='\e[0;32m'
|
||||
yellow='\e[1;33m'
|
||||
reset='\e[m'
|
||||
|
||||
if $check_dependency_versions; then
|
||||
command -v curl
|
||||
command -v jq
|
||||
@@ -78,17 +96,50 @@ if $lint; then
|
||||
if command -v cargo-deny >/dev/null; then
|
||||
cargo deny --all-features --locked --exclude-dev check licenses
|
||||
fi
|
||||
PATH="$build_dir:$PATH" "$workspace_root/build_tools/style.fish" --all --check
|
||||
PATH="$build_dir:$PATH" cargo xtask format --all --check
|
||||
for features in "" --no-default-features; do
|
||||
cargo clippy --workspace --all-targets $features
|
||||
done
|
||||
fi
|
||||
cargo test --no-default-features --workspace --all-targets
|
||||
|
||||
# When running `cargo test`, some binaries (e.g. `fish_gettext_extraction`)
|
||||
# are dynamically linked against Rust's `std-xxx.dll` instead of being
|
||||
# statically link as they usually are.
|
||||
# On Cygwin, `PATH`is not properly updated to point to the `std-xxx.dll`
|
||||
# location, so we have to do it manually.
|
||||
# See:
|
||||
# - https://github.com/rust-lang/rust/issues/149050
|
||||
# - https://github.com/msys2/MSYS2-packages/issues/5784
|
||||
(
|
||||
if $is_cygwin; then
|
||||
export PATH="$PATH:$(rustc --print target-libdir)"
|
||||
fi
|
||||
cargo test --no-default-features --workspace --all-targets
|
||||
)
|
||||
cargo test --doc --workspace
|
||||
|
||||
if $lint; then
|
||||
cargo doc --workspace --no-deps
|
||||
fi
|
||||
FISH_GETTEXT_EXTRACTION_DIR=$gettext_template_dir "$workspace_root/tests/test_driver.py" "$build_dir"
|
||||
|
||||
# Using "()" not "{}" because we do want a subshell (for the export)
|
||||
system_tests() (
|
||||
[ -n "$@" ] && export "$@"
|
||||
export FISH_GETTEXT_EXTRACTION_DIR="$gettext_template_dir"
|
||||
"$workspace_root/tests/test_driver.py" "$build_dir"
|
||||
)
|
||||
|
||||
test_cmd='FISH_GETTEXT_EXTRACTION_DIR="$gettext_template_dir" "$workspace_root/tests/test_driver.py" "$build_dir"'
|
||||
if $is_cygwin; then
|
||||
echo -e "=== Running ${green}integration tests ${yellow}with${green} symlinks${reset}"
|
||||
system_tests $cygwin_var=winsymlinks
|
||||
|
||||
echo -e "=== Running ${green}integration tests ${yellow}without${green} symlinks${reset}"
|
||||
system_tests $cygwin_var=winsymlinks
|
||||
else
|
||||
echo -e "=== Running ${green}integration tests${reset}"
|
||||
system_tests
|
||||
fi
|
||||
|
||||
exit
|
||||
}
|
||||
|
||||
@@ -12,12 +12,8 @@ begin
|
||||
# Note that this results in the file being overwritten.
|
||||
# This is desired behavior, to get rid of the results of prior invocations
|
||||
# of this script.
|
||||
begin
|
||||
echo 'msgid ""'
|
||||
echo 'msgstr ""'
|
||||
echo '"Content-Type: text/plain; charset=UTF-8\n"'
|
||||
echo ""
|
||||
end
|
||||
set -l header 'msgid ""\nmsgstr "Content-Type: text/plain; charset=UTF-8\\\\n"\n\n'
|
||||
printf $header
|
||||
|
||||
set -g workspace_root (path resolve (status dirname)/..)
|
||||
|
||||
@@ -41,8 +37,15 @@ begin
|
||||
mark_section tier1-from-rust
|
||||
|
||||
# Get rid of duplicates and sort.
|
||||
find $rust_extraction_dir -type f -exec cat {} + | msguniq --no-wrap --sort-output
|
||||
or exit 1
|
||||
begin
|
||||
# Without providing this header, msguniq complains when a msgid is non-ASCII.
|
||||
printf $header
|
||||
find $rust_extraction_dir -type f -exec cat {} +
|
||||
end |
|
||||
msguniq --no-wrap --sort-output |
|
||||
# Remove the header again. Otherwise it would appear twice, breaking the msguniq at the end
|
||||
# of this file.
|
||||
sed '/^msgid ""$/ {N; /\nmsgstr "Content-Type: text\/plain; charset=UTF-8\\\\n"$/ {N; d}}'
|
||||
|
||||
if not set -l --query _flag_use_existing_template
|
||||
rm -r $rust_extraction_dir
|
||||
|
||||
83
build_tools/make_linux_packages.sh
Executable file
83
build_tools/make_linux_packages.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script takes a source tarball (from build_tools/make_tarball.sh) and a vendor tarball (from
|
||||
# build_tools/make_vendor_tarball.sh, generated if not present), and produces:
|
||||
# * Appropriately-named symlinks to look like a Debian package
|
||||
# * Debian .changes and .dsc files with plain names ($version-1) and supported Ubuntu prefixes
|
||||
# ($version-1~somedistro)
|
||||
# * An RPM spec file
|
||||
# By default, input and output files go in ~/fish_built, but this can be controlled with the
|
||||
# FISH_ARTEFACT_PATH environment variable.
|
||||
|
||||
{
|
||||
|
||||
set -e
|
||||
|
||||
version=$1
|
||||
[ -n "$version" ] || { echo "Version number required as argument" >&2; exit 1; }
|
||||
|
||||
|
||||
[ -n "$DEB_SIGN_KEYID$DEB_SIGN_KEYFILE" ] ||
|
||||
echo "Warning: neither DEB_SIGN_KEYID or DEB_SIGN_KEYFILE environment variables are set; you
|
||||
will need a signing key for the author of the most recent debian/changelog entry." >&2
|
||||
|
||||
workpath=${FISH_ARTEFACT_PATH:-~/fish_built}
|
||||
source_tarball="$workpath"/fish-"$version".tar.xz
|
||||
vendor_tarball="$workpath"/fish-"$version"-vendor.tar.xz
|
||||
|
||||
[ -e "$source_tarball" ] || { echo "Missing source tarball, expected at $source_tarball" >&2; exit 1; }
|
||||
cd "$workpath"
|
||||
|
||||
# Unpack the sources
|
||||
tar xf "$source_tarball"
|
||||
sourcepath="$workpath"/fish-"$version"
|
||||
|
||||
# Generate the vendor tarball if it is not already present
|
||||
[ -e "$vendor_tarball" ] || (cd "$sourcepath"; build_tools/make_vendor_tarball.sh;)
|
||||
|
||||
# This step requires network access, so do it early in case it fails
|
||||
# sh has no real array support
|
||||
ubuntu_versions=$(uv run --script "$sourcepath"/build_tools/supported_ubuntu_versions.py)
|
||||
|
||||
# Write the specfile
|
||||
[ -e "$workpath"/fish.spec ] && { echo "Cowardly refusing to overwite an existing fish.spec" >&2;
|
||||
exit 1; }
|
||||
rpmversion=$(echo "$version" |sed -e 's/-/+/' -e 's/-/./g')
|
||||
sed -e "s/@version@/$version/g" -e "s/@rpmversion@/$rpmversion/g" \
|
||||
< "$sourcepath"/fish.spec.in > "$workpath"/fish.spec
|
||||
|
||||
# Make the symlinks for Debian
|
||||
ln -s "$source_tarball" "$workpath"/fish_"$version".orig.tar.xz
|
||||
ln -s "$vendor_tarball" "$workpath"/fish_"$version".orig-cargo-vendor.tar.xz
|
||||
|
||||
# Set up the Debian source tree
|
||||
cd "$sourcepath"
|
||||
mkdir cargo-vendor
|
||||
tar -C cargo-vendor -x -f "$vendor_tarball"
|
||||
cp -r contrib/debian debian
|
||||
|
||||
# The vendor tarball contains a new .cargo/config.toml, which has the
|
||||
# vendoring overrides appended to it. dpkg-source will add this as a
|
||||
# patch using the flags in debian/
|
||||
cp cargo-vendor/.cargo/config.toml .cargo/config.toml
|
||||
|
||||
# Update the Debian changelog
|
||||
# The release scripts do this for release builds - skip if it has already been done
|
||||
if head -n1 debian/changelog | grep --invert-match --quiet --fixed-strings "$version"; then
|
||||
debchange --newversion "$version-1" --distribution unstable "Snapshot build"
|
||||
fi
|
||||
|
||||
# Builds the "plain" Debian package
|
||||
# debuild runs lintian, which takes ten minutes to run over the vendor directories
|
||||
# just use dpkg-buildpackage directly
|
||||
dpkg-buildpackage --build=source -d
|
||||
|
||||
# Build the Ubuntu packages
|
||||
# deb-reversion does not work on source packages, so do the whole thing ourselves
|
||||
for series in $ubuntu_versions; do
|
||||
sed -i -e "1 s/$version-1)/$version-1~$series)/" -e "1 s/unstable/$series/" debian/changelog
|
||||
dpkg-buildpackage --build=source -d
|
||||
sed -i -e "1 s/$version-1~$series)/$version-1)/" -e "1 s/$series/unstable/" debian/changelog
|
||||
done
|
||||
|
||||
}
|
||||
@@ -15,8 +15,7 @@ tmpdir=$(mktemp -d)
|
||||
manifest=$tmpdir/Cargo.toml
|
||||
lockfile=$tmpdir/Cargo.lock
|
||||
|
||||
sed "s/^version = \".*\"\$/version = \"$VERSION\"/g" Cargo.toml \
|
||||
>"$manifest"
|
||||
sed "s/^version = \".*\"\$/version = \"$VERSION\"/g" Cargo.toml >"$manifest"
|
||||
awk -v version=$VERSION '
|
||||
/^name = "fish"$/ { ok=1 }
|
||||
ok == 1 && /^version = ".*"$/ {
|
||||
|
||||
@@ -8,20 +8,6 @@
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# We need GNU tar as that supports the --mtime and --transform options
|
||||
TAR=notfound
|
||||
for try in tar gtar gnutar; do
|
||||
if $try -Pcf /dev/null --mtime now /dev/null >/dev/null 2>&1; then
|
||||
TAR=$try
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$TAR" = "notfound" ]; then
|
||||
echo 'No suitable tar (supporting --mtime) found as tar/gtar/gnutar in PATH'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the current directory, which we'll use for telling Cargo where to find the sources
|
||||
wd="$PWD"
|
||||
|
||||
|
||||
@@ -23,6 +23,6 @@
|
||||
<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>
|
||||
<p>Enjoy! Bugs can be reported on <a href="https://github.com/fish-shell/fish-shell/">GitHub</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -10,8 +10,8 @@ fi
|
||||
|
||||
scriptname=$(basename "$0")
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "${scriptname} must be run as root"
|
||||
exit 1
|
||||
echo "${scriptname} must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
file=/etc/shells
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
echo "Removing any previous installation"
|
||||
pkgutil --pkg-info "${INSTALL_PKG_SESSION_ID}" && pkgutil --only-files --files "${INSTALL_PKG_SESSION_ID}" | while read -r installed
|
||||
do rm -v "${DSTVOLUME}${installed}"
|
||||
do rm -v "${DSTVOLUME}${installed}"
|
||||
done
|
||||
echo "... removed"
|
||||
|
||||
@@ -47,7 +47,7 @@ if test -z "$CI" || [ "$(git -C "$workspace_root" tag | wc -l)" -gt 1 ]; then {
|
||||
num_authors=$(wc -l <"$relnotes_tmp/committers-now")
|
||||
num_new_authors=$(wc -l <"$relnotes_tmp/committers-new")
|
||||
printf %s \
|
||||
"This release comprises $num_commits commits since $previous_version," \
|
||||
"This release brings $num_commits new commits since $previous_version," \
|
||||
" contributed by $num_authors authors, $num_new_authors of which are new committers."
|
||||
echo
|
||||
echo
|
||||
|
||||
@@ -90,7 +90,7 @@ Created by ./build_tools/release.sh $version"
|
||||
}
|
||||
|
||||
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
|
||||
cargo fetch --offline
|
||||
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.
|
||||
@@ -110,6 +110,8 @@ 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
|
||||
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
#!/usr/bin/env fish
|
||||
#
|
||||
# This runs Python files, fish scripts (*.fish), and Rust files
|
||||
# through their respective code formatting programs.
|
||||
#
|
||||
# `--all`: Format all eligible files instead of the ones specified as arguments.
|
||||
# `--check`: Instead of reformatting, fail if a file is not formatted correctly.
|
||||
# `--force`: Proceed without asking if uncommitted changes are detected.
|
||||
# Only relevant if `--all` is specified but `--check` is not specified.
|
||||
|
||||
set -l fish_files
|
||||
set -l python_files
|
||||
set -l rust_files
|
||||
set -l all no
|
||||
|
||||
argparse all check force -- $argv
|
||||
or exit $status
|
||||
|
||||
if set -l -q _flag_all
|
||||
set all yes
|
||||
if set -q argv[1]
|
||||
echo "Unexpected arguments: '$argv'"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
# Potential for false positives: Not all fish files are formatted, see the `fish_files`
|
||||
# definition below.
|
||||
set -l relevant_uncommitted_changes (git status --porcelain --short --untracked-files=all | sed -e 's/^ *[^ ]* *//' | grep -E '.*\.(fish|py|rs)$')
|
||||
if set -q relevant_uncommitted_changes[1]
|
||||
for changed_file in $relevant_uncommitted_changes
|
||||
echo $changed_file
|
||||
end
|
||||
echo
|
||||
echo 'You have uncommitted changes (listed above). Are you sure you want to restyle?'
|
||||
read -P 'y/N? ' -n1 -l ans
|
||||
if not string match -qi y -- $ans
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
set fish_files $workspace_root/{benchmarks,build_tools,etc,share}/**.fish
|
||||
set python_files $workspace_root
|
||||
else
|
||||
# Format the files specified as arguments.
|
||||
set -l files $argv
|
||||
set fish_files (string match -r '^.*\.fish$' -- $files)
|
||||
set python_files (string match -r '^.*\.py$' -- $files)
|
||||
set rust_files (string match -r '^.*\.rs$' -- $files)
|
||||
end
|
||||
|
||||
set -l red (set_color red)
|
||||
set -l green (set_color green)
|
||||
set -l yellow (set_color yellow)
|
||||
set -l normal (set_color normal)
|
||||
|
||||
function die -V red -V normal
|
||||
echo $red$argv[1]$normal
|
||||
exit 1
|
||||
end
|
||||
|
||||
if set -q fish_files[1]
|
||||
if not type -q fish_indent
|
||||
echo
|
||||
echo $yellow'Could not find `fish_indent` in `$PATH`.'$normal
|
||||
exit 127
|
||||
end
|
||||
echo === Running "$green"fish_indent"$normal"
|
||||
if set -l -q _flag_check
|
||||
fish_indent --check -- $fish_files
|
||||
or die "Fish files are not formatted correctly."
|
||||
else
|
||||
fish_indent -w -- $fish_files
|
||||
end
|
||||
end
|
||||
|
||||
if set -q python_files[1]
|
||||
if not type -q ruff
|
||||
echo
|
||||
echo $yellow'Please install `ruff` to style python'$normal
|
||||
exit 127
|
||||
end
|
||||
echo === Running "$green"ruff format"$normal"
|
||||
if set -l -q _flag_check
|
||||
ruff format --check $python_files
|
||||
or die "Python files are not formatted correctly."
|
||||
else
|
||||
ruff format $python_files
|
||||
end
|
||||
end
|
||||
|
||||
if test $all = yes; or set -q rust_files[1]
|
||||
if not cargo fmt --version >/dev/null
|
||||
echo
|
||||
echo $yellow'Please install "rustfmt" to style Rust, e.g. via:'
|
||||
echo "rustup component add rustfmt"$normal
|
||||
exit 127
|
||||
end
|
||||
|
||||
set -l edition_spec string match -r '^edition\s*=.*'
|
||||
test "$($edition_spec <Cargo.toml)" = "$($edition_spec <.rustfmt.toml)"
|
||||
or die "Cargo.toml and .rustfmt.toml use different editions"
|
||||
|
||||
echo === Running "$green"rustfmt"$normal"
|
||||
if set -l -q _flag_check
|
||||
if test $all = yes
|
||||
cargo fmt --all --check
|
||||
else
|
||||
rustfmt --check --files-with-diff $rust_files
|
||||
end
|
||||
or die "Rust files are not formatted correctly."
|
||||
else
|
||||
if test $all = yes
|
||||
cargo fmt --all
|
||||
else
|
||||
rustfmt $rust_files
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -13,9 +13,31 @@ sort --version-sort </dev/null
|
||||
# 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() {
|
||||
@@ -23,14 +45,14 @@ from_gh() {
|
||||
path=$2
|
||||
destination=$3
|
||||
contents=$(curl -fsS https://raw.githubusercontent.com/"${repo}"/refs/heads/master/"${path}")
|
||||
printf '%s\n' >"$destination" "$contents"
|
||||
printf '%s\n' "$contents" >"$destination"
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
@@ -18,14 +18,14 @@ set(VARS_FOR_CARGO_SPHINX_WRAPPER
|
||||
|
||||
add_custom_target(sphinx-docs
|
||||
COMMAND env ${VARS_FOR_CARGO_SPHINX_WRAPPER}
|
||||
cargo xtask html-docs --fish-indent=${FISH_INDENT_FOR_BUILDING_DOCS}
|
||||
${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
|
||||
COMMAND env ${VARS_FOR_CARGO_SPHINX_WRAPPER}
|
||||
cargo xtask man-pages
|
||||
${Rust_CARGO} xtask man-pages
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Building man pages with Sphinx")
|
||||
|
||||
@@ -51,9 +51,10 @@ endif()
|
||||
add_feature_info(Documentation WITH_DOCS "user manual and documentation")
|
||||
|
||||
if(WITH_DOCS)
|
||||
add_custom_target(doc ALL
|
||||
DEPENDS sphinx-docs sphinx-manpages)
|
||||
add_custom_target(doc ALL DEPENDS sphinx-docs sphinx-manpages)
|
||||
# Group docs targets into a DocsTargets folder
|
||||
set_property(TARGET doc sphinx-docs sphinx-manpages
|
||||
PROPERTY FOLDER cmake/DocTargets)
|
||||
set_property(
|
||||
TARGET doc sphinx-docs sphinx-manpages
|
||||
PROPERTY FOLDER cmake/DocTargets
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -22,17 +22,17 @@ foreach(_VAR ${_Rust_USER_VARS})
|
||||
endforeach()
|
||||
|
||||
if (NOT DEFINED Rust_CARGO_CACHED)
|
||||
find_program(Rust_CARGO_CACHED cargo PATHS "$ENV{HOME}/.cargo/bin")
|
||||
find_program(Rust_CARGO_CACHED cargo PATHS "$ENV{HOME}/.cargo/bin")
|
||||
endif()
|
||||
|
||||
if (NOT EXISTS "${Rust_CARGO_CACHED}")
|
||||
message(FATAL_ERROR "The cargo executable ${Rust_CARGO_CACHED} was not found. "
|
||||
"Consider setting `Rust_CARGO_CACHED` to the absolute path of `cargo`."
|
||||
)
|
||||
message(FATAL_ERROR "The cargo executable ${Rust_CARGO_CACHED} was not found. "
|
||||
"Consider setting `Rust_CARGO_CACHED` to the absolute path of `cargo`."
|
||||
)
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED Rust_COMPILER_CACHED)
|
||||
find_program(Rust_COMPILER_CACHED rustc PATHS "$ENV{HOME}/.cargo/bin")
|
||||
find_program(Rust_COMPILER_CACHED rustc PATHS "$ENV{HOME}/.cargo/bin")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -45,31 +45,31 @@ endif()
|
||||
# Figure out the target by just using the host target.
|
||||
# If you want to cross-compile, you'll have to set Rust_CARGO_TARGET
|
||||
if(NOT Rust_CARGO_TARGET_CACHED)
|
||||
execute_process(
|
||||
COMMAND "${Rust_COMPILER_CACHED}" --version --verbose
|
||||
OUTPUT_VARIABLE _RUSTC_VERSION_RAW
|
||||
RESULT_VARIABLE _RUSTC_VERSION_RESULT
|
||||
)
|
||||
|
||||
if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" ))
|
||||
message(FATAL_ERROR "Failed to get rustc version.\n"
|
||||
"${Rust_COMPILER} --version failed with error: `${_RUSTC_VERSION_RESULT}`")
|
||||
endif()
|
||||
|
||||
if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n")
|
||||
set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
|
||||
execute_process(
|
||||
COMMAND "${Rust_COMPILER_CACHED}" --version --verbose
|
||||
OUTPUT_VARIABLE _RUSTC_VERSION_RAW
|
||||
RESULT_VARIABLE _RUSTC_VERSION_RESULT
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
message(FATAL_ERROR "CMake is in cross-compiling mode."
|
||||
"Manually set `Rust_CARGO_TARGET`."
|
||||
)
|
||||
endif()
|
||||
set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple")
|
||||
if(NOT ( "${_RUSTC_VERSION_RESULT}" EQUAL "0" ))
|
||||
message(FATAL_ERROR "Failed to get rustc version.\n"
|
||||
"${Rust_COMPILER} --version failed with error: `${_RUSTC_VERSION_RESULT}`")
|
||||
endif()
|
||||
|
||||
if (_RUSTC_VERSION_RAW MATCHES "host: ([a-zA-Z0-9_\\-]*)\n")
|
||||
set(Rust_DEFAULT_HOST_TARGET "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"Failed to parse rustc host target. `rustc --version --verbose` evaluated to:\n${_RUSTC_VERSION_RAW}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
message(FATAL_ERROR "CMake is in cross-compiling mode."
|
||||
"Manually set `Rust_CARGO_TARGET`."
|
||||
)
|
||||
endif()
|
||||
set(Rust_CARGO_TARGET_CACHED "${Rust_DEFAULT_HOST_TARGET}" CACHE STRING "Target triple")
|
||||
endif()
|
||||
|
||||
# Set the input variables as non-cache variables so that the variables are available after
|
||||
|
||||
@@ -14,32 +14,36 @@ set(rel_completionsdir "fish/vendor_completions.d")
|
||||
set(rel_functionsdir "fish/vendor_functions.d")
|
||||
set(rel_confdir "fish/vendor_conf.d")
|
||||
|
||||
set(extra_completionsdir
|
||||
"${datadir}/${rel_completionsdir}"
|
||||
CACHE STRING "Path for extra completions")
|
||||
set(
|
||||
extra_completionsdir "${datadir}/${rel_completionsdir}"
|
||||
CACHE STRING "Path for extra completions"
|
||||
)
|
||||
|
||||
set(extra_functionsdir
|
||||
"${datadir}/${rel_functionsdir}"
|
||||
CACHE STRING "Path for extra functions")
|
||||
set(
|
||||
extra_functionsdir "${datadir}/${rel_functionsdir}"
|
||||
CACHE STRING "Path for extra functions"
|
||||
)
|
||||
|
||||
set(extra_confdir
|
||||
"${datadir}/${rel_confdir}"
|
||||
CACHE STRING "Path for extra configuration")
|
||||
set(
|
||||
extra_confdir "${datadir}/${rel_confdir}"
|
||||
CACHE STRING "Path for extra configuration"
|
||||
)
|
||||
|
||||
|
||||
# These are the man pages that go in system manpath; all manpages go in the fish-specific manpath.
|
||||
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
|
||||
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.
|
||||
@@ -48,40 +52,49 @@ set(MANUALS ${SPHINX_OUTPUT_DIR}/man/man1/fish.1
|
||||
# On other operating systems, don't install a realpath man page, as they almost all have a realpath
|
||||
# command, while macOS does not.
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set(CONDEMNED_PAGE "open.1")
|
||||
set(CONDEMNED_PAGE "open.1")
|
||||
else()
|
||||
set(CONDEMNED_PAGE "realpath.1")
|
||||
set(CONDEMNED_PAGE "realpath.1")
|
||||
endif()
|
||||
|
||||
# Define a function to help us create directories.
|
||||
function(FISH_CREATE_DIRS)
|
||||
foreach(dir ${ARGV})
|
||||
install(DIRECTORY DESTINATION ${dir})
|
||||
endforeach(dir)
|
||||
foreach(dir ${ARGV})
|
||||
install(DIRECTORY DESTINATION ${dir})
|
||||
endforeach(dir)
|
||||
endfunction(FISH_CREATE_DIRS)
|
||||
|
||||
function(FISH_TRY_CREATE_DIRS)
|
||||
foreach(dir ${ARGV})
|
||||
if(NOT IS_ABSOLUTE ${dir})
|
||||
set(abs_dir "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${dir}")
|
||||
else()
|
||||
set(abs_dir "\$ENV{DESTDIR}${dir}")
|
||||
endif()
|
||||
install(SCRIPT CODE "EXECUTE_PROCESS(COMMAND mkdir -p ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
|
||||
execute_process(COMMAND chmod 755 ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
|
||||
")
|
||||
endforeach()
|
||||
foreach(dir ${ARGV})
|
||||
if(NOT IS_ABSOLUTE ${dir})
|
||||
set(abs_dir "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${dir}")
|
||||
else()
|
||||
set(abs_dir "\$ENV{DESTDIR}${dir}")
|
||||
endif()
|
||||
install(SCRIPT CODE "
|
||||
EXECUTE_PROCESS(COMMAND mkdir -p ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
|
||||
execute_process(COMMAND chmod 755 ${abs_dir} OUTPUT_QUIET ERROR_QUIET)
|
||||
")
|
||||
endforeach()
|
||||
endfunction(FISH_TRY_CREATE_DIRS)
|
||||
|
||||
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fish
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ
|
||||
GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
|
||||
DESTINATION ${bindir})
|
||||
install(
|
||||
PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/fish
|
||||
PERMISSIONS
|
||||
OWNER_READ
|
||||
OWNER_WRITE
|
||||
OWNER_EXECUTE
|
||||
GROUP_READ
|
||||
GROUP_EXECUTE
|
||||
WORLD_READ
|
||||
WORLD_EXECUTE
|
||||
DESTINATION ${bindir}
|
||||
)
|
||||
|
||||
if(NOT IS_ABSOLUTE ${bindir})
|
||||
set(abs_bindir "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${bindir}")
|
||||
set(abs_bindir "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${bindir}")
|
||||
else()
|
||||
set(abs_bindir "\$ENV{DESTDIR}${bindir}")
|
||||
set(abs_bindir "\$ENV{DESTDIR}${bindir}")
|
||||
endif()
|
||||
install(CODE "file(CREATE_LINK ${abs_bindir}/fish ${abs_bindir}/fish_indent)")
|
||||
install(CODE "file(CREATE_LINK ${abs_bindir}/fish ${abs_bindir}/fish_key_reader)")
|
||||
@@ -90,87 +103,116 @@ fish_create_dirs(${sysconfdir}/fish/conf.d ${sysconfdir}/fish/completions
|
||||
${sysconfdir}/fish/functions)
|
||||
install(FILES etc/config.fish DESTINATION ${sysconfdir}/fish/)
|
||||
|
||||
fish_create_dirs(${rel_datadir}/fish ${rel_datadir}/fish/completions
|
||||
${rel_datadir}/fish/functions
|
||||
${rel_datadir}/fish/man/man1 ${rel_datadir}/fish/tools
|
||||
${rel_datadir}/fish/tools/web_config
|
||||
${rel_datadir}/fish/tools/web_config/js
|
||||
${rel_datadir}/fish/prompts
|
||||
${rel_datadir}/fish/themes
|
||||
)
|
||||
fish_create_dirs(
|
||||
${rel_datadir}/fish ${rel_datadir}/fish/completions
|
||||
${rel_datadir}/fish/functions
|
||||
${rel_datadir}/fish/man/man1 ${rel_datadir}/fish/tools
|
||||
${rel_datadir}/fish/tools/web_config
|
||||
${rel_datadir}/fish/tools/web_config/js
|
||||
${rel_datadir}/fish/prompts
|
||||
${rel_datadir}/fish/themes
|
||||
)
|
||||
|
||||
configure_file(share/__fish_build_paths.fish.in share/__fish_build_paths.fish)
|
||||
install(FILES share/config.fish
|
||||
${CMAKE_CURRENT_BINARY_DIR}/share/__fish_build_paths.fish
|
||||
DESTINATION ${rel_datadir}/fish)
|
||||
${CMAKE_CURRENT_BINARY_DIR}/share/__fish_build_paths.fish
|
||||
DESTINATION ${rel_datadir}/fish
|
||||
)
|
||||
|
||||
# Create only the vendor directories inside the prefix (#5029 / #6508)
|
||||
fish_create_dirs(${rel_datadir}/fish/vendor_completions.d ${rel_datadir}/fish/vendor_functions.d
|
||||
${rel_datadir}/fish/vendor_conf.d)
|
||||
fish_create_dirs(
|
||||
${rel_datadir}/fish/vendor_completions.d
|
||||
${rel_datadir}/fish/vendor_functions.d
|
||||
${rel_datadir}/fish/vendor_conf.d
|
||||
)
|
||||
|
||||
fish_try_create_dirs(${rel_datadir}/pkgconfig)
|
||||
configure_file(fish.pc.in fish.pc.noversion @ONLY)
|
||||
|
||||
add_custom_command(OUTPUT fish.pc
|
||||
add_custom_command(
|
||||
OUTPUT fish.pc
|
||||
COMMAND sed '/Version/d' fish.pc.noversion > fish.pc
|
||||
COMMAND printf "Version: " >> fish.pc
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh >> fish.pc
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${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)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fish.pc
|
||||
DESTINATION ${rel_datadir}/pkgconfig)
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/fish.pc
|
||||
DESTINATION ${rel_datadir}/pkgconfig
|
||||
)
|
||||
|
||||
install(DIRECTORY share/completions/
|
||||
DESTINATION ${rel_datadir}/fish/completions
|
||||
FILES_MATCHING PATTERN "*.fish")
|
||||
install(
|
||||
DIRECTORY share/completions/
|
||||
DESTINATION ${rel_datadir}/fish/completions
|
||||
FILES_MATCHING PATTERN "*.fish"
|
||||
)
|
||||
|
||||
install(DIRECTORY share/functions/
|
||||
DESTINATION ${rel_datadir}/fish/functions
|
||||
FILES_MATCHING PATTERN "*.fish")
|
||||
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/prompts/
|
||||
DESTINATION ${rel_datadir}/fish/prompts
|
||||
FILES_MATCHING PATTERN "*.fish"
|
||||
)
|
||||
|
||||
install(DIRECTORY share/themes/
|
||||
DESTINATION ${rel_datadir}/fish/themes
|
||||
FILES_MATCHING PATTERN "*.theme")
|
||||
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 ${SPHINX_OUTPUT_DIR}/man/man1/
|
||||
DESTINATION ${rel_datadir}/fish/man/man1
|
||||
FILES_MATCHING
|
||||
PATTERN "*.1"
|
||||
PATTERN ${CONDEMNED_PAGE} EXCLUDE)
|
||||
install(
|
||||
DIRECTORY ${SPHINX_OUTPUT_DIR}/man/man1/
|
||||
DESTINATION ${rel_datadir}/fish/man/man1
|
||||
FILES_MATCHING
|
||||
PATTERN "*.1"
|
||||
PATTERN ${CONDEMNED_PAGE} EXCLUDE
|
||||
)
|
||||
|
||||
install(PROGRAMS share/tools/create_manpage_completions.py
|
||||
DESTINATION ${rel_datadir}/fish/tools/)
|
||||
install(
|
||||
PROGRAMS share/tools/create_manpage_completions.py
|
||||
DESTINATION ${rel_datadir}/fish/tools/
|
||||
)
|
||||
|
||||
install(DIRECTORY share/tools/web_config
|
||||
DESTINATION ${rel_datadir}/fish/tools/
|
||||
FILES_MATCHING
|
||||
PATTERN "*.png"
|
||||
PATTERN "*.css"
|
||||
PATTERN "*.html"
|
||||
PATTERN "*.py"
|
||||
PATTERN "*.js")
|
||||
install(
|
||||
DIRECTORY share/tools/web_config
|
||||
DESTINATION ${rel_datadir}/fish/tools/
|
||||
FILES_MATCHING
|
||||
PATTERN "*.png"
|
||||
PATTERN "*.css"
|
||||
PATTERN "*.html"
|
||||
PATTERN "*.py"
|
||||
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 ${SPHINX_OUTPUT_DIR}/html/ # Trailing slash is important!
|
||||
DESTINATION ${docdir} OPTIONAL)
|
||||
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
|
||||
PROPERTY FOLDER cmake/InstallTargets)
|
||||
set_property(
|
||||
TARGET build_fish_pc
|
||||
PROPERTY FOLDER cmake/InstallTargets
|
||||
)
|
||||
|
||||
# Make a target build_root that installs into the buildroot directory, for testing.
|
||||
set(BUILDROOT_DIR ${CMAKE_CURRENT_BINARY_DIR}/buildroot)
|
||||
add_custom_target(build_root
|
||||
COMMAND DESTDIR=${BUILDROOT_DIR} ${CMAKE_COMMAND}
|
||||
--build ${CMAKE_CURRENT_BINARY_DIR} --target install)
|
||||
add_custom_target(
|
||||
build_root
|
||||
COMMAND DESTDIR=${BUILDROOT_DIR} ${CMAKE_COMMAND}
|
||||
--build ${CMAKE_CURRENT_BINARY_DIR} --target install
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
set(FISH_USE_SYSTEM_PCRE2 ON CACHE BOOL
|
||||
"Try to use PCRE2 from the system, instead of the pcre2-sys version")
|
||||
"Try to use PCRE2 from the system, instead of the pcre2-sys version"
|
||||
)
|
||||
|
||||
if(FISH_USE_SYSTEM_PCRE2)
|
||||
message(STATUS "Trying to use PCRE2 from the system")
|
||||
message(STATUS "Trying to use PCRE2 from the system")
|
||||
else()
|
||||
message(STATUS "Forcing static build of PCRE2")
|
||||
set(FISH_PCRE2_BUILDFLAG "PCRE2_SYS_STATIC=1")
|
||||
message(STATUS "Forcing static build of PCRE2")
|
||||
set(FISH_PCRE2_BUILDFLAG "PCRE2_SYS_STATIC=1")
|
||||
endif(FISH_USE_SYSTEM_PCRE2)
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
FILE(GLOB FISH_CHECKS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/checks/*.fish)
|
||||
foreach(CHECK ${FISH_CHECKS})
|
||||
get_filename_component(CHECK_NAME ${CHECK} NAME)
|
||||
add_custom_target(
|
||||
test_${CHECK_NAME}
|
||||
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
|
||||
USES_TERMINAL
|
||||
)
|
||||
get_filename_component(CHECK_NAME ${CHECK} NAME)
|
||||
add_custom_target(
|
||||
test_${CHECK_NAME}
|
||||
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
|
||||
USES_TERMINAL
|
||||
)
|
||||
endforeach(CHECK)
|
||||
|
||||
FILE(GLOB PEXPECTS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/pexpects/*.py)
|
||||
foreach(PEXPECT ${PEXPECTS})
|
||||
get_filename_component(PEXPECT ${PEXPECT} NAME)
|
||||
add_custom_target(
|
||||
test_${PEXPECT}
|
||||
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
|
||||
USES_TERMINAL
|
||||
)
|
||||
get_filename_component(PEXPECT ${PEXPECT} NAME)
|
||||
add_custom_target(
|
||||
test_${PEXPECT}
|
||||
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
|
||||
USES_TERMINAL
|
||||
)
|
||||
endforeach(PEXPECT)
|
||||
|
||||
# Rust stuff.
|
||||
@@ -30,14 +30,20 @@ if(DEFINED ASAN)
|
||||
# Rust w/ -Zsanitizer=address requires explicitly specifying the --target triple or else linker
|
||||
# errors pertaining to asan symbols will ensue.
|
||||
if(NOT DEFINED Rust_CARGO_TARGET)
|
||||
message(FATAL_ERROR "ASAN requires defining the CMake variable Rust_CARGO_TARGET to the
|
||||
intended target triple")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"ASAN requires defining the CMake variable Rust_CARGO_TARGET to the
|
||||
intended target triple"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED TSAN)
|
||||
if(NOT DEFINED Rust_CARGO_TARGET)
|
||||
message(FATAL_ERROR "TSAN requires defining the CMake variable Rust_CARGO_TARGET to the
|
||||
intended target triple")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"TSAN requires defining the CMake variable Rust_CARGO_TARGET to the
|
||||
intended target triple"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -53,10 +59,10 @@ endif()
|
||||
|
||||
# The top-level test target is "fish_run_tests".
|
||||
add_custom_target(fish_run_tests
|
||||
# TODO: This should be replaced with a unified solution, possibly build_tools/check.sh.
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${max_concurrency_flag} ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND env ${VARS_FOR_CARGO}
|
||||
${Rust_CARGO}
|
||||
# TODO: This should be replaced with a unified solution, possibly build_tools/check.sh.
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/tests/test_driver.py ${max_concurrency_flag} ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND
|
||||
env ${VARS_FOR_CARGO} ${Rust_CARGO}
|
||||
test
|
||||
--no-default-features
|
||||
--features=${FISH_CARGO_FEATURES}
|
||||
@@ -64,7 +70,7 @@ add_custom_target(fish_run_tests
|
||||
--workspace
|
||||
--target-dir ${rust_target_dir}
|
||||
${cargo_test_flags}
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
DEPENDS fish fish_indent fish_key_reader
|
||||
USES_TERMINAL
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
DEPENDS fish fish_indent fish_key_reader
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
fish (4.6.0-1) stable; urgency=medium
|
||||
|
||||
* Release of new version 4.6.0.
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/4.6.0 for details.
|
||||
|
||||
-- Johannes Altmanninger <aclopte@gmail.com> Sat, 28 Mar 2026 12:56:37 +0800
|
||||
|
||||
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.
|
||||
|
||||
@@ -13,7 +13,7 @@ Build-Depends: debhelper-compat (= 13),
|
||||
python3-sphinx,
|
||||
# Test dependencies
|
||||
locales-all,
|
||||
man-db,
|
||||
man-db | man,
|
||||
python3
|
||||
# 4.6.2 is Debian 12/Ubuntu Noble 24.04; Ubuntu Jammy is 4.6.0.1
|
||||
Standards-Version: 4.6.2
|
||||
@@ -26,8 +26,8 @@ Architecture: any
|
||||
# for col and lock
|
||||
Depends: bsdextrautils,
|
||||
file,
|
||||
# for man
|
||||
man-db,
|
||||
# for showing built-in help pages
|
||||
man-db | man,
|
||||
# for kill
|
||||
procps,
|
||||
python3 (>=3.5),
|
||||
|
||||
@@ -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:
|
||||
|
||||
33
contrib/shell.nix
Normal file
33
contrib/shell.nix
Normal file
@@ -0,0 +1,33 @@
|
||||
# Environment containing all dependencies needed for
|
||||
# - building fish,
|
||||
# - building documentation,
|
||||
# - running all tests,
|
||||
# - formatting and checking lints.
|
||||
#
|
||||
# enter interactive bash shell:
|
||||
# nix-shell contrib/shell.nix
|
||||
#
|
||||
# using system nixpkgs (otherwise fetches pinned version):
|
||||
# nix-shell contrib/shell.nix --arg pkgs 'import <nixpkgs> {}'
|
||||
#
|
||||
# run single command:
|
||||
# nix-shell contrib/shell --run "cargo xtask check"
|
||||
{ pkgs ? (import (builtins.fetchTarball {
|
||||
url = "https://github.com/NixOS/nixpkgs/archive/nixos-25.11.tar.gz";
|
||||
sha256 = "1ia5kjykm9xmrpwbzhbaf4cpwi3yaxr7shl6amj8dajvgbyh2yh4";
|
||||
}) { }), ... }:
|
||||
pkgs.mkShell {
|
||||
buildInputs = [
|
||||
(pkgs.python3.withPackages (pyPkgs: [ pyPkgs.pexpect ]))
|
||||
pkgs.cargo
|
||||
pkgs.clippy
|
||||
pkgs.cmake
|
||||
pkgs.gettext
|
||||
pkgs.pcre2
|
||||
pkgs.procps # tests use pgrep/pkill
|
||||
pkgs.ruff
|
||||
pkgs.rustc
|
||||
pkgs.rustfmt
|
||||
pkgs.sphinx
|
||||
];
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -38,6 +38,14 @@ pub fn fish_doc_dir() -> Cow<'static, Path> {
|
||||
cargo_target_dir().join("fish-docs").into()
|
||||
}
|
||||
|
||||
fn l10n_dir() -> Cow<'static, Path> {
|
||||
workspace_root().join("localization").into()
|
||||
}
|
||||
|
||||
pub fn po_dir() -> Cow<'static, Path> {
|
||||
l10n_dir().join("po").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());
|
||||
|
||||
@@ -43,7 +43,7 @@ fn build_man(sec1_dir: &Path) {
|
||||
];
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ fn build_man(sec1_dir: &Path) {
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
assert_ne!(
|
||||
env_var("FISH_BUILD_DOCS"),
|
||||
Some("1".to_string()),
|
||||
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."
|
||||
);
|
||||
|
||||
@@ -8,9 +8,14 @@ license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
fish-feature-flags.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
libc.workspace = true
|
||||
nix.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
fish-build-helper.workspace = true
|
||||
rsconf.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
5
crates/common/build.rs
Normal file
5
crates/common/build.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use fish_build_helper::target_os_is_apple;
|
||||
|
||||
fn main() {
|
||||
rsconf::declare_cfg("apple", target_os_is_apple());
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,12 +8,12 @@
|
||||
use std::cmp;
|
||||
use std::sync::{
|
||||
LazyLock,
|
||||
atomic::{AtomicIsize, Ordering},
|
||||
atomic::{AtomicUsize, 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`.
|
||||
pub static FISH_AMBIGUOUS_WIDTH: AtomicIsize = AtomicIsize::new(1);
|
||||
pub static FISH_AMBIGUOUS_WIDTH: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
/// Width of emoji characters.
|
||||
///
|
||||
@@ -25,68 +25,33 @@
|
||||
/// Valid values are 1, and 2. 1 is the typical emoji width used in Unicode 8 while some newer
|
||||
/// terminals use a width of 2 since Unicode 9.
|
||||
// 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);
|
||||
pub static FISH_EMOJI_WIDTH: AtomicUsize = AtomicUsize::new(2);
|
||||
|
||||
static WC_LOOKUP_TABLE: LazyLock<WcLookupTable> = LazyLock::new(WcLookupTable::new);
|
||||
|
||||
/// A safe wrapper around the system `wcwidth()` function
|
||||
#[cfg(not(cygwin))]
|
||||
pub fn wcwidth(c: char) -> isize {
|
||||
unsafe extern "C" {
|
||||
pub unsafe fn wcwidth(c: libc::wchar_t) -> libc::c_int;
|
||||
}
|
||||
|
||||
const {
|
||||
assert!(size_of::<libc::wchar_t>() >= size_of::<char>());
|
||||
}
|
||||
|
||||
let width = unsafe { wcwidth(c as libc::wchar_t) };
|
||||
isize::try_from(width).unwrap()
|
||||
}
|
||||
|
||||
// Big hack to use our versions of wcswidth where we know them to be broken, which is
|
||||
// EVERYWHERE (https://github.com/fish-shell/fish-shell/issues/2199)
|
||||
pub fn fish_wcwidth(c: char) -> isize {
|
||||
// The system version of wcwidth should accurately reflect the ability to represent characters
|
||||
// in the console session, but knows nothing about the capabilities of other terminal emulators
|
||||
// or ttys. Use it from the start only if we are logged in to the physical console.
|
||||
#[cfg(not(cygwin))]
|
||||
if fish_common::is_console_session() {
|
||||
return wcwidth(c);
|
||||
}
|
||||
|
||||
pub fn fish_wcwidth(c: char) -> Option<usize> {
|
||||
// Check for VS16 which selects emoji presentation. This "promotes" a character like U+2764
|
||||
// (width 1) to an emoji (probably width 2). So treat it as width 1 so the sums work. See #2652.
|
||||
// VS15 selects text presentation.
|
||||
let variation_selector_16 = '\u{FE0F}';
|
||||
let variation_selector_15 = '\u{FE0E}';
|
||||
if c == variation_selector_16 {
|
||||
return 1;
|
||||
return Some(1);
|
||||
} else if c == variation_selector_15 {
|
||||
return 0;
|
||||
return Some(0);
|
||||
}
|
||||
|
||||
// Check for Emoji_Modifier property. Only the Fitzpatrick modifiers have this, in range
|
||||
// 1F3FB..1F3FF. This is a hack because such an emoji appearing on its own would be drawn as
|
||||
// width 2, but that's unlikely to be useful. See #8275.
|
||||
if ('\u{1F3FB}'..='\u{1F3FF}').contains(&c) {
|
||||
return 0;
|
||||
return Some(0);
|
||||
}
|
||||
|
||||
let width = WC_LOOKUP_TABLE.classify(c);
|
||||
match width {
|
||||
WcWidth::NonCharacter | WcWidth::NonPrint | WcWidth::Combining | WcWidth::Unassigned => {
|
||||
#[cfg(not(cygwin))]
|
||||
{
|
||||
// Fall back to system wcwidth in this case.
|
||||
wcwidth(c)
|
||||
}
|
||||
#[cfg(cygwin)]
|
||||
{
|
||||
// No system wcwidth for UTF-32 on cygwin.
|
||||
0
|
||||
}
|
||||
}
|
||||
Some(match width {
|
||||
WcWidth::NonPrint => return None,
|
||||
WcWidth::NonCharacter | WcWidth::Combining | WcWidth::Unassigned => 0,
|
||||
WcWidth::Ambiguous | WcWidth::PrivateUse => {
|
||||
// TR11: "All private-use characters are by default classified as Ambiguous".
|
||||
FISH_AMBIGUOUS_WIDTH.load(Ordering::Relaxed)
|
||||
@@ -94,26 +59,25 @@ pub fn fish_wcwidth(c: char) -> isize {
|
||||
WcWidth::One => 1,
|
||||
WcWidth::Two => 2,
|
||||
WcWidth::WidenedIn9 => FISH_EMOJI_WIDTH.load(Ordering::Relaxed),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if
|
||||
/// the system one is busted.
|
||||
pub fn fish_wcswidth(s: &wstr) -> isize {
|
||||
// ascii fast path; empty iterator returns true for .all()
|
||||
if s.chars().all(|c| c.is_ascii() && !c.is_ascii_control()) {
|
||||
return s.len() as isize;
|
||||
pub fn fish_wcswidth(s: &wstr) -> Option<usize> {
|
||||
fish_wcswidth_canonicalizing(s, std::convert::identity)
|
||||
}
|
||||
|
||||
pub fn fish_wcswidth_canonicalizing(s: &wstr, canonicalize: fn(char) -> char) -> Option<usize> {
|
||||
let chars = s.chars().map(canonicalize);
|
||||
// ascii fast path
|
||||
if chars.clone().all(|c| c.is_ascii() && !c.is_ascii_control()) {
|
||||
return Some(s.len());
|
||||
}
|
||||
|
||||
let mut result = 0;
|
||||
for c in s.chars() {
|
||||
let w = fish_wcwidth(c);
|
||||
if w < 0 {
|
||||
return -1;
|
||||
}
|
||||
result += w;
|
||||
for c in chars {
|
||||
result += fish_wcwidth(c)?;
|
||||
}
|
||||
result
|
||||
Some(result)
|
||||
}
|
||||
|
||||
pub fn wcscasecmp(lhs: &wstr, rhs: &wstr) -> cmp::Ordering {
|
||||
|
||||
13
crates/feature-flags/Cargo.toml
Normal file
13
crates/feature-flags/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "fish-feature-flags"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
version = "0.0.0"
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-widestring.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1,15 +1,14 @@
|
||||
//! Flags to enable upcoming features
|
||||
|
||||
use crate::prelude::*;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[cfg(test)]
|
||||
use std::cell::RefCell;
|
||||
use fish_widestring::{L, WExt as _, wstr};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
/// The list of flags.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum FeatureFlag {
|
||||
/// Whether ^ is supported for stderr redirection.
|
||||
StderrNoCaret,
|
||||
@@ -63,10 +62,10 @@ pub struct FeatureMetadata {
|
||||
pub description: &'static wstr,
|
||||
|
||||
/// Default flag value.
|
||||
pub default_value: bool,
|
||||
default_value: bool,
|
||||
|
||||
/// Whether the value can still be changed or not.
|
||||
pub read_only: bool,
|
||||
read_only: bool,
|
||||
}
|
||||
|
||||
/// The metadata, indexed by flag.
|
||||
@@ -131,9 +130,11 @@ pub struct FeatureMetadata {
|
||||
flag: FeatureFlag::IgnoreTerminfo,
|
||||
name: L!("ignore-terminfo"),
|
||||
groups: L!("4.1"),
|
||||
description: L!("do not look up $TERM in terminfo database"),
|
||||
description: L!(
|
||||
"do not look up $TERM in terminfo database (historical, can no longer be changed)"
|
||||
),
|
||||
default_value: true,
|
||||
read_only: false,
|
||||
read_only: true,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::QueryTerm,
|
||||
@@ -154,31 +155,26 @@ pub struct FeatureMetadata {
|
||||
];
|
||||
|
||||
thread_local!(
|
||||
#[cfg(test)]
|
||||
static LOCAL_FEATURES: RefCell<Option<Features>> = const { RefCell::new(None) };
|
||||
static LOCAL_OVERRIDE_STACK: RefCell<Vec<(FeatureFlag, bool)>> =
|
||||
const { RefCell::new(Vec::new()) };
|
||||
);
|
||||
|
||||
/// The singleton shared feature set.
|
||||
static FEATURES: Features = Features::new();
|
||||
|
||||
/// Perform a feature test on the global set of features.
|
||||
pub fn test(flag: FeatureFlag) -> bool {
|
||||
#[cfg(test)]
|
||||
{
|
||||
LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).test(flag))
|
||||
pub fn feature_test(flag: FeatureFlag) -> bool {
|
||||
if let Some(value) = LOCAL_OVERRIDE_STACK.with(|stack| {
|
||||
for &(overridden_feature, value) in stack.borrow().iter().rev() {
|
||||
if flag == overridden_feature {
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
None
|
||||
}) {
|
||||
return value;
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
FEATURES.test(flag)
|
||||
}
|
||||
}
|
||||
|
||||
pub use test as feature_test;
|
||||
|
||||
/// Set a flag.
|
||||
#[cfg(test)]
|
||||
pub fn set(flag: FeatureFlag, value: bool) {
|
||||
LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).set(flag, value));
|
||||
FEATURES.test(flag)
|
||||
}
|
||||
|
||||
/// Parses a comma-separated feature-flag string, updating ourselves with the values.
|
||||
@@ -186,38 +182,31 @@ pub fn set(flag: FeatureFlag, value: bool) {
|
||||
/// The special group name "all" may be used for those who like to live on the edge.
|
||||
/// Unknown features are silently ignored.
|
||||
pub fn set_from_string<'a>(str: impl Into<&'a wstr>) {
|
||||
let wstr: &wstr = str.into();
|
||||
#[cfg(test)]
|
||||
{
|
||||
LOCAL_FEATURES.with(|fc| {
|
||||
fc.borrow()
|
||||
.as_ref()
|
||||
.unwrap_or(&FEATURES)
|
||||
.set_from_string(wstr);
|
||||
});
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
{
|
||||
FEATURES.set_from_string(wstr);
|
||||
}
|
||||
FEATURES.set_from_string(str.into());
|
||||
}
|
||||
|
||||
impl Features {
|
||||
const fn new() -> Self {
|
||||
Features {
|
||||
values: [
|
||||
AtomicBool::new(METADATA[0].default_value),
|
||||
AtomicBool::new(METADATA[1].default_value),
|
||||
AtomicBool::new(METADATA[2].default_value),
|
||||
AtomicBool::new(METADATA[3].default_value),
|
||||
AtomicBool::new(METADATA[4].default_value),
|
||||
AtomicBool::new(METADATA[5].default_value),
|
||||
AtomicBool::new(METADATA[6].default_value),
|
||||
AtomicBool::new(METADATA[7].default_value),
|
||||
AtomicBool::new(METADATA[8].default_value),
|
||||
AtomicBool::new(METADATA[9].default_value),
|
||||
],
|
||||
}
|
||||
// TODO: feature(const_array): use std::array::from_fn()
|
||||
use std::mem::{MaybeUninit, transmute};
|
||||
let values = {
|
||||
let mut data: [MaybeUninit<AtomicBool>; METADATA.len()] =
|
||||
[const { MaybeUninit::uninit() }; METADATA.len()];
|
||||
|
||||
let mut i = 0;
|
||||
while i < METADATA.len() {
|
||||
data[i].write(AtomicBool::new(METADATA[i].default_value));
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// SAFETY: `data` is guaranteed initialized by the loop
|
||||
unsafe {
|
||||
transmute::<[MaybeUninit<AtomicBool>; METADATA.len()], [AtomicBool; METADATA.len()]>(
|
||||
data,
|
||||
)
|
||||
}
|
||||
};
|
||||
Features { values }
|
||||
}
|
||||
|
||||
fn test(&self, flag: FeatureFlag) -> bool {
|
||||
@@ -229,19 +218,14 @@ fn set(&self, flag: FeatureFlag, value: bool) {
|
||||
}
|
||||
|
||||
fn set_from_string(&self, str: &wstr) {
|
||||
let whitespace = L!("\t\n\0x0B\0x0C\r ").as_char_slice();
|
||||
for entry in str.as_char_slice().split(|c| *c == ',') {
|
||||
for entry in str.split(',') {
|
||||
let entry = entry.trim();
|
||||
if entry.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Trim leading and trailing whitespace
|
||||
let entry = &entry[entry.iter().take_while(|c| whitespace.contains(c)).count()..];
|
||||
let entry =
|
||||
&entry[..entry.len() - entry.iter().take_while(|c| whitespace.contains(c)).count()];
|
||||
|
||||
// A "no-" prefix inverts the sense.
|
||||
let (name, value) = match entry.strip_prefix(L!("no-").as_char_slice()) {
|
||||
let (name, value) = match entry.strip_prefix("no-") {
|
||||
Some(suffix) => (suffix, false),
|
||||
None => (entry, true),
|
||||
};
|
||||
@@ -267,28 +251,20 @@ fn set_from_string(&self, str: &wstr) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn scoped_test(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
|
||||
LOCAL_FEATURES.with(|fc| {
|
||||
assert!(
|
||||
fc.borrow().is_none(),
|
||||
"scoped_test() does not support nesting"
|
||||
);
|
||||
|
||||
let f = Features::new();
|
||||
f.set(flag, value);
|
||||
*fc.borrow_mut() = Some(f);
|
||||
|
||||
/// Run code with a feature overridden.
|
||||
/// This should only be used in tests.
|
||||
pub fn with_overridden_feature(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) {
|
||||
LOCAL_OVERRIDE_STACK.with(|stack| {
|
||||
stack.borrow_mut().push((flag, value));
|
||||
test_fn();
|
||||
|
||||
*fc.borrow_mut() = None;
|
||||
stack.borrow_mut().pop();
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{FeatureFlag, Features, METADATA, scoped_test, set, test};
|
||||
use crate::prelude::*;
|
||||
use super::{FeatureFlag, Features, METADATA, feature_test, with_overridden_feature};
|
||||
use fish_widestring::L;
|
||||
|
||||
#[test]
|
||||
fn test_feature_flags() {
|
||||
@@ -314,25 +290,19 @@ fn test_feature_flags() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scoped() {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
assert!(test(FeatureFlag::QuestionMarkNoGlob));
|
||||
fn test_overridden_feature() {
|
||||
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
assert!(feature_test(FeatureFlag::QuestionMarkNoGlob));
|
||||
});
|
||||
|
||||
set(FeatureFlag::QuestionMarkNoGlob, true);
|
||||
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, false, || {
|
||||
assert!(!test(FeatureFlag::QuestionMarkNoGlob));
|
||||
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, false, || {
|
||||
assert!(!feature_test(FeatureFlag::QuestionMarkNoGlob));
|
||||
});
|
||||
|
||||
set(FeatureFlag::QuestionMarkNoGlob, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_nested_scopes_not_supported() {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
scoped_test(FeatureFlag::QuestionMarkNoGlob, false, || {});
|
||||
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, false, || {
|
||||
with_overridden_feature(FeatureFlag::QuestionMarkNoGlob, true, || {
|
||||
assert!(feature_test(FeatureFlag::QuestionMarkNoGlob));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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') {
|
||||
|
||||
@@ -11,24 +11,16 @@ fn main() {
|
||||
PathBuf::from(fish_build_helper::fish_build_dir()).join("fish-localization-map-cache");
|
||||
embed_localizations(&cache_dir);
|
||||
|
||||
fish_build_helper::rebuild_if_path_changed(
|
||||
fish_build_helper::workspace_root()
|
||||
.join("localization")
|
||||
.join("po"),
|
||||
);
|
||||
fish_build_helper::rebuild_if_path_changed(fish_build_helper::po_dir());
|
||||
}
|
||||
|
||||
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()
|
||||
.join("localization")
|
||||
.join("po");
|
||||
|
||||
// Ensure that the directory is created, because clippy cannot compile the code if the
|
||||
// directory does not exist.
|
||||
std::fs::create_dir_all(cache_dir).unwrap();
|
||||
@@ -56,7 +48,7 @@ fn embed_localizations(cache_dir: &Path) {
|
||||
Ok(output) => {
|
||||
let has_check_format =
|
||||
String::from_utf8_lossy(&output.stdout).contains("--check-format");
|
||||
for dir_entry_result in po_dir.read_dir().unwrap() {
|
||||
for dir_entry_result in fish_build_helper::po_dir().read_dir().unwrap() {
|
||||
let dir_entry = dir_entry_result.unwrap();
|
||||
let po_file_path = dir_entry.path();
|
||||
if po_file_path.extension() != Some(OsStr::new("po")) {
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
type Catalog = &'static phf::Map<&'static str, &'static str>;
|
||||
|
||||
static LANGUAGE_PRECEDENCE: LazyLock<Mutex<Vec<(&'static str, Catalog)>>> =
|
||||
LazyLock::new(|| Mutex::new(vec![]));
|
||||
static LANGUAGE_PRECEDENCE: Mutex<Vec<(&'static str, Catalog)>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn gettext(message_str: &'static str) -> Option<&'static str> {
|
||||
let language_precedence = LANGUAGE_PRECEDENCE.lock().unwrap();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
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;
|
||||
|
||||
@@ -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(
|
||||
@@ -400,8 +400,10 @@ fn test_char() {
|
||||
|
||||
#[test]
|
||||
fn test_ptr() {
|
||||
assert_fmt!("%p", core::ptr::null::<u8>() => "0");
|
||||
assert_fmt!("%p", 0xDEADBEEF_usize as *const u8 => "0xdeadbeef");
|
||||
assert_fmt!("%p", core::ptr::null::<()>() => "0");
|
||||
|
||||
let tmp = core::ptr::without_provenance::<()>(0xDEADBEEF);
|
||||
assert_fmt!("%p", tmp => "0xdeadbeef");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -7,7 +7,10 @@ repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
errno.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
libc.workspace = true
|
||||
nix.workspace = true
|
||||
rand.workspace = true
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
//! Generic utilities library.
|
||||
|
||||
use errno::errno;
|
||||
use fish_widestring::prelude::*;
|
||||
use rand::{SeedableRng, rngs::SmallRng};
|
||||
use std::cmp::Ordering;
|
||||
use std::time;
|
||||
use rand::{SeedableRng as _, rngs::SmallRng};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
ffi::CStr,
|
||||
io::Write as _,
|
||||
os::fd::{BorrowedFd, RawFd},
|
||||
time,
|
||||
};
|
||||
|
||||
/// Compares two wide character strings with an (arguably) intuitive ordering. This function tries
|
||||
/// to order strings in a way which is intuitive to humans with regards to sorting strings
|
||||
@@ -234,6 +240,26 @@ fn wcsfilecmp_leading_digits(a: &wstr, b: &wstr) -> (Ordering, usize, usize) {
|
||||
(ret, ai, bi)
|
||||
}
|
||||
|
||||
pub fn write_to_fd(input: &[u8], fd: RawFd) -> nix::Result<usize> {
|
||||
nix::unistd::write(unsafe { BorrowedFd::borrow_raw(fd) }, input)
|
||||
}
|
||||
|
||||
/// Prints the provided string, followed by a colon, space, and the string representation of the
|
||||
/// current errno via [`libc::strerror`].
|
||||
pub fn perror(s: &str) {
|
||||
let e = errno().0;
|
||||
let mut stderr = std::io::stderr().lock();
|
||||
if !s.is_empty() {
|
||||
let _ = write!(stderr, "{s}: ");
|
||||
}
|
||||
let slice = unsafe {
|
||||
let msg = libc::strerror(e);
|
||||
CStr::from_ptr(msg).to_bytes()
|
||||
};
|
||||
let _ = stderr.write_all(slice);
|
||||
let _ = stderr.write_all(b"\n");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::wcsfilecmp;
|
||||
|
||||
@@ -7,7 +7,6 @@ repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
fish-common.workspace = true
|
||||
fish-fallback.workspace = true
|
||||
fish-widestring.workspace = true
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//! Helper functions for working with wcstring.
|
||||
|
||||
use fish_common::{get_ellipsis_char, get_ellipsis_str};
|
||||
use fish_fallback::{fish_wcwidth, lowercase, lowercase_rev, wcscasecmp, wcscasecmp_fuzzy};
|
||||
use fish_widestring::{decode_byte_from_char, prelude::*};
|
||||
use fish_widestring::{ELLIPSIS_CHAR, prelude::*};
|
||||
|
||||
/// Return the number of newlines in a string.
|
||||
pub fn count_newlines(s: &wstr) -> usize {
|
||||
@@ -336,30 +335,6 @@ pub fn string_fuzzy_match_string(
|
||||
StringFuzzyMatch::try_create(string, match_against, anchor_start)
|
||||
}
|
||||
|
||||
/// Implementation of wcs2bytes that accepts a callback.
|
||||
/// The first argument can be either a `&str` or `&wstr`.
|
||||
/// This invokes `func` with byte slices containing the UTF-8 encoding of the characters in the
|
||||
/// input, doing one invocation per character.
|
||||
/// If `func` returns false, it stops; otherwise it continues.
|
||||
/// Return false if the callback returned false, otherwise true.
|
||||
pub fn str2bytes_callback(input: impl IntoCharIter, mut func: impl FnMut(&[u8]) -> bool) -> bool {
|
||||
// A `char` represents an Unicode scalar value, which takes up at most 4 bytes when encoded in UTF-8.
|
||||
let mut converted = [0_u8; 4];
|
||||
|
||||
for c in input.chars() {
|
||||
let bytes = if let Some(byte) = decode_byte_from_char(c) {
|
||||
converted[0] = byte;
|
||||
&converted[..=0]
|
||||
} else {
|
||||
c.encode_utf8(&mut converted).as_bytes()
|
||||
};
|
||||
if !func(bytes) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Split a string by runs of any of the separator characters provided in `seps`.
|
||||
/// Note the delimiters are the characters in `seps`, not `seps` itself.
|
||||
/// `seps` may contain the NUL character.
|
||||
@@ -473,32 +448,13 @@ pub fn split_about<'haystack>(
|
||||
output
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum EllipsisType {
|
||||
None,
|
||||
// Prefer niceness over minimalness
|
||||
Prettiest,
|
||||
// Make every character count ($ instead of ...)
|
||||
Shortest,
|
||||
}
|
||||
|
||||
pub fn truncate(input: &wstr, max_len: usize, etype: Option<EllipsisType>) -> WString {
|
||||
let etype = etype.unwrap_or(EllipsisType::Prettiest);
|
||||
// TODO: This should work on render width rather than the number of codepoints.
|
||||
pub fn truncate(input: &wstr, max_len: usize) -> WString {
|
||||
if input.len() <= max_len {
|
||||
return input.to_owned();
|
||||
}
|
||||
|
||||
if etype == EllipsisType::None {
|
||||
return input[..max_len].to_owned();
|
||||
}
|
||||
if etype == EllipsisType::Prettiest {
|
||||
let ellipsis_str = get_ellipsis_str();
|
||||
let mut output = input[..max_len - ellipsis_str.len()].to_owned();
|
||||
output += ellipsis_str;
|
||||
return output;
|
||||
}
|
||||
let mut output = input[..max_len - 1].to_owned();
|
||||
output.push(get_ellipsis_char());
|
||||
output.push(ELLIPSIS_CHAR);
|
||||
output
|
||||
}
|
||||
|
||||
@@ -565,12 +521,12 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Like fish_wcwidth, but returns 0 for characters with no real width instead of -1.
|
||||
/// Like fish_wcwidth, but returns 0 for characters with no real width instead of none.
|
||||
pub fn fish_wcwidth_visible(c: char) -> isize {
|
||||
if c == '\x08' {
|
||||
return -1;
|
||||
}
|
||||
fish_wcwidth(c).max(0)
|
||||
fish_wcwidth(c).unwrap_or_default().try_into().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -7,6 +7,8 @@ repository.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
libc.workspace = true
|
||||
unicode-width.workspace = true
|
||||
widestring.workspace = true
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -6,13 +6,34 @@
|
||||
|
||||
pub mod word_char;
|
||||
|
||||
use std::{iter, slice};
|
||||
use std::{
|
||||
ffi::{CStr, CString, OsStr, OsString},
|
||||
iter,
|
||||
os::unix::ffi::{OsStrExt as _, OsStringExt as _},
|
||||
slice,
|
||||
};
|
||||
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};
|
||||
}
|
||||
|
||||
// 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}';
|
||||
|
||||
/// The character to use where the text has been truncated.
|
||||
pub const ELLIPSIS_CHAR: char = '\u{2026}'; // ('…')
|
||||
|
||||
pub const SPECIAL_KEY_ENCODE_BASE: char = '\u{F500}';
|
||||
// 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
|
||||
@@ -25,9 +46,79 @@ pub mod prelude {
|
||||
// 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_BASE: char = char_offset(SPECIAL_KEY_ENCODE_BASE, 256);
|
||||
pub const ENCODE_DIRECT_END: char = char_offset(ENCODE_DIRECT_BASE, 256);
|
||||
|
||||
// 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);
|
||||
|
||||
/// Character representing any character except '/' (slash).
|
||||
pub const ANY_CHAR: char = char_offset(WILDCARD_RESERVED_BASE, 0);
|
||||
/// Character representing any character string not containing '/' (slash).
|
||||
pub const ANY_STRING: char = char_offset(WILDCARD_RESERVED_BASE, 1);
|
||||
/// Character representing any character string.
|
||||
pub const ANY_STRING_RECURSIVE: char = char_offset(WILDCARD_RESERVED_BASE, 2);
|
||||
/// This is a special pseudo-char that is not used other than to mark the
|
||||
/// end of the special characters so we can sanity check the enum range.
|
||||
#[allow(dead_code)]
|
||||
pub const ANY_SENTINEL: char = char_offset(WILDCARD_RESERVED_BASE, 3);
|
||||
|
||||
/// Character representing a home directory.
|
||||
pub const HOME_DIRECTORY: char = char_offset(EXPAND_RESERVED_BASE, 0);
|
||||
/// Character representing process expansion for %self.
|
||||
pub const PROCESS_EXPAND_SELF: char = char_offset(EXPAND_RESERVED_BASE, 1);
|
||||
/// Character representing variable expansion.
|
||||
pub const VARIABLE_EXPAND: char = char_offset(EXPAND_RESERVED_BASE, 2);
|
||||
/// Character representing variable expansion into a single element.
|
||||
pub const VARIABLE_EXPAND_SINGLE: char = char_offset(EXPAND_RESERVED_BASE, 3);
|
||||
/// Character representing the start of a bracket expansion.
|
||||
pub const BRACE_BEGIN: char = char_offset(EXPAND_RESERVED_BASE, 4);
|
||||
/// Character representing the end of a bracket expansion.
|
||||
pub const BRACE_END: char = char_offset(EXPAND_RESERVED_BASE, 5);
|
||||
/// Character representing separation between two bracket elements.
|
||||
pub const BRACE_SEP: char = char_offset(EXPAND_RESERVED_BASE, 6);
|
||||
/// Character that takes the place of any whitespace within non-quoted text in braces
|
||||
pub const BRACE_SPACE: char = char_offset(EXPAND_RESERVED_BASE, 7);
|
||||
/// Separate subtokens in a token with this character.
|
||||
pub const INTERNAL_SEPARATOR: char = char_offset(EXPAND_RESERVED_BASE, 8);
|
||||
/// Character representing an empty variable expansion. Only used transitively while expanding
|
||||
/// variables.
|
||||
pub const VARIABLE_EXPAND_EMPTY: char = char_offset(EXPAND_RESERVED_BASE, 9);
|
||||
|
||||
const _: () = assert!(
|
||||
EXPAND_RESERVED_END as u32 > VARIABLE_EXPAND_EMPTY as u32,
|
||||
"Characters used in expansions must stay within private use area"
|
||||
);
|
||||
|
||||
/// The string represented by PROCESS_EXPAND_SELF
|
||||
pub const PROCESS_EXPAND_SELF_STR: &wstr = L!("%self");
|
||||
|
||||
/// Return true if the character is in a range reserved for fish's private use.
|
||||
///
|
||||
/// NOTE: This is used when tokenizing the input. It is also used when reading input, before
|
||||
/// tokenization, to replace such chars with REPLACEMENT_WCHAR if they're not part of a quoted
|
||||
/// string. We don't want external input to be able to feed reserved characters into our
|
||||
/// lexer/parser or code evaluator.
|
||||
//
|
||||
// TODO: Actually implement the replacement as documented above.
|
||||
pub fn fish_reserved_codepoint(c: char) -> bool {
|
||||
(c >= RESERVED_CHAR_BASE && c < RESERVED_CHAR_END)
|
||||
|| (c >= SPECIAL_KEY_ENCODE_BASE && c < ENCODE_DIRECT_END)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// UTF-8 by the terminal. If we were to interpret each of those bytes as a codepoint and encode it
|
||||
@@ -40,6 +131,86 @@ pub fn encode_byte_to_char(byte: u8) -> char {
|
||||
.expect("private-use codepoint should be valid char")
|
||||
}
|
||||
|
||||
/// Returns a newly allocated multibyte character string equivalent of the specified wide character
|
||||
/// string.
|
||||
///
|
||||
/// This function decodes illegal character sequences in a reversible way using the private use
|
||||
/// area.
|
||||
pub fn wcs2bytes(input: impl IntoCharIter) -> Vec<u8> {
|
||||
let mut result = vec![];
|
||||
wcs2bytes_appending(&mut result, input);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn wcs2osstring(input: &wstr) -> OsString {
|
||||
if input.is_empty() {
|
||||
return OsString::new();
|
||||
}
|
||||
|
||||
let mut result = vec![];
|
||||
wcs2bytes_appending(&mut result, input);
|
||||
OsString::from_vec(result)
|
||||
}
|
||||
|
||||
/// Same as [`wcs2bytes`]. Meant to be used when we need a zero-terminated string to feed legacy APIs.
|
||||
/// Note: if `input` contains any interior NUL bytes, the result will be truncated at the first!
|
||||
pub fn wcs2zstring(input: &wstr) -> CString {
|
||||
if input.is_empty() {
|
||||
return CString::default();
|
||||
}
|
||||
|
||||
let mut vec = Vec::with_capacity(input.len() + 1);
|
||||
str2bytes_callback(input, |buff| {
|
||||
vec.extend_from_slice(buff);
|
||||
true
|
||||
});
|
||||
vec.push(b'\0');
|
||||
|
||||
match CString::from_vec_with_nul(vec) {
|
||||
Ok(cstr) => cstr,
|
||||
Err(err) => {
|
||||
// `input` contained a NUL in the middle; we can retrieve `vec`, though
|
||||
let mut vec = err.into_bytes();
|
||||
let pos = vec.iter().position(|c| *c == b'\0').unwrap();
|
||||
vec.truncate(pos + 1);
|
||||
// Safety: We truncated after the first NUL
|
||||
unsafe { CString::from_vec_with_nul_unchecked(vec) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`wcs2bytes`], but appends to `output` instead of returning a new string.
|
||||
pub fn wcs2bytes_appending(output: &mut Vec<u8>, input: impl IntoCharIter) {
|
||||
str2bytes_callback(input, |buff| {
|
||||
output.extend_from_slice(buff);
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
/// Implementation of wcs2bytes that accepts a callback.
|
||||
/// The first argument can be either a `&str` or `&wstr`.
|
||||
/// This invokes `func` with byte slices containing the UTF-8 encoding of the characters in the
|
||||
/// input, doing one invocation per character.
|
||||
/// If `func` returns false, it stops; otherwise it continues.
|
||||
/// Return false if the callback returned false, otherwise true.
|
||||
pub fn str2bytes_callback(input: impl IntoCharIter, mut func: impl FnMut(&[u8]) -> bool) -> bool {
|
||||
// A `char` represents an Unicode scalar value, which takes up at most 4 bytes when encoded in UTF-8.
|
||||
let mut converted = [0_u8; 4];
|
||||
|
||||
for c in input.chars() {
|
||||
let bytes = if let Some(byte) = decode_byte_from_char(c) {
|
||||
converted[0] = byte;
|
||||
&converted[..=0]
|
||||
} else {
|
||||
c.encode_utf8(&mut converted).as_bytes()
|
||||
};
|
||||
if !func(bytes) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Decode a literal byte from a UTF-32 character.
|
||||
pub fn decode_byte_from_char(c: char) -> Option<u8> {
|
||||
if c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END {
|
||||
@@ -53,6 +224,278 @@ pub fn decode_byte_from_char(c: char) -> Option<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to make it more convenient to pass ascii/Unicode strings to functions that can take
|
||||
/// non-Unicode values. The result is nul-terminated and can be passed to OS functions.
|
||||
///
|
||||
/// This is only implemented for owned types where an owned instance will skip allocations (e.g.
|
||||
/// `CString` can return `self`) but not implemented for owned instances where a new allocation is
|
||||
/// always required (e.g. implemented for `&wstr` but not `WideString`) because you might as well be
|
||||
/// left with the original item if we're going to allocate from scratch in all cases.
|
||||
pub trait ToCString {
|
||||
/// Correctly convert to a nul-terminated [`CString`] that can be passed to OS functions.
|
||||
fn to_cstring(self) -> CString;
|
||||
}
|
||||
|
||||
impl ToCString for CString {
|
||||
fn to_cstring(self) -> CString {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCString for &CStr {
|
||||
fn to_cstring(self) -> CString {
|
||||
self.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Safely converts from `&wstr` to a `CString` to a nul-terminated `CString` that can be passed to
|
||||
/// OS functions, taking into account non-Unicode values that have been shifted into the private-use
|
||||
/// range by using [`wcs2zstring()`].
|
||||
impl ToCString for &wstr {
|
||||
/// The wide string may contain non-Unicode bytes mapped to the private-use Unicode range, so we
|
||||
/// have to use [`wcs2zstring()`](self::wcs2zstring) to convert it correctly.
|
||||
fn to_cstring(self) -> CString {
|
||||
self::wcs2zstring(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Safely converts from `&WString` to a nul-terminated `CString` that can be passed to OS
|
||||
/// functions, taking into account non-Unicode values that have been shifted into the private-use
|
||||
/// range by using [`wcs2zstring()`].
|
||||
impl ToCString for &WString {
|
||||
fn to_cstring(self) -> CString {
|
||||
self.as_utfstr().to_cstring()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a (probably ascii) string to CString that can be passed to OS functions.
|
||||
impl ToCString for Vec<u8> {
|
||||
fn to_cstring(mut self) -> CString {
|
||||
self.push(b'\0');
|
||||
CString::from_vec_with_nul(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a (probably ascii) string to nul-terminated CString that can be passed to OS functions.
|
||||
impl ToCString for &[u8] {
|
||||
fn to_cstring(self) -> CString {
|
||||
CString::new(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod decoder {
|
||||
use crate::{ENCODE_DIRECT_BASE, ENCODE_DIRECT_END, char_offset, wstr};
|
||||
use buffer::Buffer;
|
||||
use std::{char::REPLACEMENT_CHARACTER, ops::Range};
|
||||
use widestring::utfstr::CharsUtf32;
|
||||
|
||||
mod buffer {
|
||||
// The size required for a PUA-encoded character from our special PUA range - 1,
|
||||
// since that is the maximum number characters our look-ahead needs to check.
|
||||
const MAX_SIZE: usize = 2;
|
||||
pub(super) struct Buffer {
|
||||
buffer: [char; MAX_SIZE],
|
||||
length: usize,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub(super) fn empty() -> Self {
|
||||
Self {
|
||||
buffer: ['\0'; MAX_SIZE],
|
||||
length: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn push(&mut self, c: char) {
|
||||
self.buffer[self.length] = c;
|
||||
self.length += 1;
|
||||
}
|
||||
|
||||
pub(super) fn pop(&mut self) -> Option<char> {
|
||||
if self.length == 0 {
|
||||
return None;
|
||||
}
|
||||
self.length -= 1;
|
||||
Some(self.buffer[self.length])
|
||||
}
|
||||
|
||||
pub(super) fn pop_front(&mut self) -> Option<char> {
|
||||
if self.length == 0 {
|
||||
return None;
|
||||
}
|
||||
self.buffer.rotate_left(1);
|
||||
self.length -= 1;
|
||||
Some(self.buffer[MAX_SIZE - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PUA_ENCODE_RANGE: Range<char> = ENCODE_DIRECT_BASE..ENCODE_DIRECT_END;
|
||||
const ENCODED_PUA_CHAR_FIRST_CHAR: char = char_offset(ENCODE_DIRECT_BASE, 0xef);
|
||||
// The second UTF-8 byte of a character in our special PUA range is in the range
|
||||
// 0x98..0x9c.
|
||||
const ENCODED_PUA_CHAR_SECOND_CHAR_RANGE: Range<char> =
|
||||
char_offset(ENCODE_DIRECT_BASE, 0x98)..char_offset(ENCODE_DIRECT_BASE, 0x9c);
|
||||
|
||||
/// This serves as the data container for building a double-ended iterator which decodes our
|
||||
/// PUA-encoded chars into a char iterator where each encoded non-UTF-8 byte is replaced by the
|
||||
/// replacement character, and each encoded PUA codepoint is turned back into a single char
|
||||
/// whose value is the original PUA codepoint.
|
||||
///
|
||||
/// The latter part makes the decoding logic somewhat complicated, because encoded PUA chars
|
||||
/// take up 3 chars in our encoding. Therefore, in some cases, we need to take more than 1 char
|
||||
/// from the `encoded_chars` iterator before we know whether to decode 3 chars together into a
|
||||
/// single char, or whether the chars should be replaced by the replacement char individually.
|
||||
/// In cases where we took more than 1 char and then notice that individual replacement is
|
||||
/// warranted, we return a replacement char for the first char we took from the iterator, and
|
||||
/// cache the 1 or 2 other chars we read in `buffer_front` or `buffer_back`, depending on the
|
||||
/// reading direction. Buffers store elements in such an order that getting the next character
|
||||
/// requires `pop` when using the buffer associated with the current reading direction, and
|
||||
/// `pop_front` when using the other buffer. This is done to optimize the common case of
|
||||
/// iterating in a single direction.
|
||||
///
|
||||
/// The buffers have to be considered before taking more chars from `encoded_chars`.
|
||||
/// If the iterator is only read in one direction, the buffer for the other direction will not
|
||||
/// be used. But because it's possible that the iterator is read from both ends, it can happen
|
||||
/// that when `encoded_chars` runs out, the buffer for the opposite reading direction is
|
||||
/// non-empty. In the [`Self::next`] and [`Self::next_back`] implementations, this logic is
|
||||
/// encapsulated into closures for getting the next char from the appropriate source.
|
||||
/// At most 2 chars will ever be stored in a buffer, so they are implemented using a fixed-size
|
||||
/// array, requiring no heap allocations.
|
||||
///
|
||||
/// Note that in most cases, we can avoid using the buffers, and simply forward the char
|
||||
/// obtained from `encoded_chars`. Only chars in [`PUA_ENCODE_RANGE`] can possibly encode PUA
|
||||
/// chars, so if we read any other char, we know that it's not part of such an encoding and can
|
||||
/// return it directly. If we read in the forward direction, we can also exploit knowledge about
|
||||
/// the possible values of our PUA encoding. Specifically, the first char in such an encoding
|
||||
/// will always be [`ENCODED_PUA_CHAR_FIRST_CHAR`], and the second char will be in the range
|
||||
/// [`ENCODED_PUA_CHAR_SECOND_CHAR_RANGE`].
|
||||
pub(super) struct Decoder<'a> {
|
||||
encoded_chars: CharsUtf32<'a>,
|
||||
buffer_front: Buffer,
|
||||
buffer_back: Buffer,
|
||||
}
|
||||
|
||||
impl<'a> Decoder<'a> {
|
||||
pub(super) fn new(encoded_str: &'a wstr) -> Self {
|
||||
Self {
|
||||
encoded_chars: encoded_str.chars(),
|
||||
buffer_front: Buffer::empty(),
|
||||
buffer_back: Buffer::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_pua_decoding(encoding: &[char; 3]) -> Option<char> {
|
||||
let mut bytes = [0u8; 3];
|
||||
for (index, &c) in encoding.iter().enumerate() {
|
||||
bytes[index] = super::decode_byte_from_char(c)?;
|
||||
}
|
||||
let first_decoded_char =
|
||||
std::str::from_utf8(&bytes).ok()?.chars().next().expect(
|
||||
"Non-empty byte slice which is valid UTF-8 must result in at least one char.",
|
||||
);
|
||||
// For strings whose width we compute, we only expect invalid UTF-8 and codepoints from the
|
||||
// PUA encoding range to be PUA encoded.
|
||||
// If we reach this point, the encoded bytes are valid UTF-8, so the only remaining
|
||||
// expected case are codepoints from the PUA encoding range.
|
||||
// These all take 3 bytes to represent in UTF-8, so if we check that the first parsed
|
||||
// codepoint is in the expected range, we know that exactly 3 bytes were consumed for
|
||||
// parsing this codepoint.
|
||||
assert!(PUA_ENCODE_RANGE.contains(&first_decoded_char));
|
||||
Some(first_decoded_char)
|
||||
}
|
||||
|
||||
fn replace_if_pua_encoded(c: char) -> char {
|
||||
if PUA_ENCODE_RANGE.contains(&c) {
|
||||
REPLACEMENT_CHARACTER
|
||||
} else {
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Decoder<'a> {
|
||||
type Item = char;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut get_next_char = || {
|
||||
self.buffer_front
|
||||
.pop()
|
||||
.or_else(|| self.encoded_chars.next())
|
||||
.or_else(|| self.buffer_back.pop_front())
|
||||
};
|
||||
let c_0 = get_next_char()?;
|
||||
if c_0 != ENCODED_PUA_CHAR_FIRST_CHAR {
|
||||
return Some(replace_if_pua_encoded(c_0));
|
||||
}
|
||||
if let Some(c_1) = get_next_char() {
|
||||
if ENCODED_PUA_CHAR_SECOND_CHAR_RANGE.contains(&c_1) {
|
||||
if let Some(c_2) = get_next_char() {
|
||||
if let Some(decoded_pua_char) = try_pua_decoding(&[c_0, c_1, c_2]) {
|
||||
return Some(decoded_pua_char);
|
||||
}
|
||||
self.buffer_front.push(c_2);
|
||||
}
|
||||
}
|
||||
self.buffer_front.push(c_1);
|
||||
}
|
||||
// If decoding 3 consecutive PUA chars into the encoded PUA char fails, `c_0` should be
|
||||
// returned. `c_0` is `ENCODED_PUA_CHAR_FIRST_CHAR` if we reach this point, so return
|
||||
// the `REPLACEMENT_CHARACTER` in these cases.
|
||||
Some(REPLACEMENT_CHARACTER)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for Decoder<'a> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
let mut get_next_char = || {
|
||||
self.buffer_back
|
||||
.pop()
|
||||
.or_else(|| self.encoded_chars.next_back())
|
||||
.or_else(|| self.buffer_front.pop_front())
|
||||
};
|
||||
let c_2 = get_next_char()?;
|
||||
if !PUA_ENCODE_RANGE.contains(&c_2) {
|
||||
return Some(c_2);
|
||||
}
|
||||
if let Some(c_1) = get_next_char() {
|
||||
if PUA_ENCODE_RANGE.contains(&c_1) {
|
||||
if let Some(c_0) = get_next_char() {
|
||||
if let Some(decoded_pua_char) = try_pua_decoding(&[c_0, c_1, c_2]) {
|
||||
return Some(decoded_pua_char);
|
||||
}
|
||||
self.buffer_back.push(c_0);
|
||||
}
|
||||
self.buffer_back.push(c_1);
|
||||
}
|
||||
}
|
||||
// If decoding 3 consecutive PUA chars into the encoded PUA char fails, `c_2` should be
|
||||
// returned. `c_2` is in `PUA_ENCODE_RANGE` if we reach this point, so return the
|
||||
// `REPLACEMENT_CHARACTER` in these cases.
|
||||
Some(REPLACEMENT_CHARACTER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Only exists for tests. Exported because the encoding functionality is not available in this
|
||||
/// crate. Do not use for non-testing purposes.
|
||||
pub fn decode_with_replacement(encoded_str: &wstr) -> impl DoubleEndedIterator<Item = char> {
|
||||
decoder::Decoder::new(encoded_str)
|
||||
}
|
||||
|
||||
/// Takes a PUA-encoded string, decodes it by restoring encoded PUA codepoints and replacing encoded
|
||||
/// non-UTF-8 bytes by the replacement character U+FFFD.
|
||||
/// The result is passed to the [`unicode_width`] crate, which will compute its width, which will be
|
||||
/// the return value of this function.
|
||||
pub fn decoded_width(encoded_str: &wstr) -> usize {
|
||||
// TODO: Avoid constructing String by using `unicode_width::char_iter_width` once that is
|
||||
// available in a released version of the crate (it's already on the crate's master branch).
|
||||
use unicode_width::UnicodeWidthStr as _;
|
||||
decoder::Decoder::new(encoded_str)
|
||||
.collect::<String>()
|
||||
.width()
|
||||
}
|
||||
|
||||
pub const fn char_offset(base: char, offset: u32) -> char {
|
||||
match char::from_u32(base as u32 + offset) {
|
||||
Some(c) => c,
|
||||
@@ -60,6 +503,82 @@ pub const fn char_offset(base: char, offset: u32) -> char {
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes the bytes in `input` into a [`WString`], encoding non-UTF-8 bytes into private-use-area
|
||||
/// code-points. Bytes which would be parsed into our reserved PUA range are encoded individually,
|
||||
/// to allow for correct round-tripping.
|
||||
pub fn bytes2wcstring(mut input: &[u8]) -> WString {
|
||||
if input.is_empty() {
|
||||
return WString::new();
|
||||
}
|
||||
|
||||
let mut result = WString::with_capacity(input.len());
|
||||
|
||||
fn append_escaped_str(output: &mut WString, input: &str) {
|
||||
for (i, c) in input.char_indices() {
|
||||
if fish_reserved_codepoint(c) {
|
||||
for byte in &input.as_bytes()[i..i + c.len_utf8()] {
|
||||
output.push(encode_byte_to_char(*byte));
|
||||
}
|
||||
} else {
|
||||
output.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while !input.is_empty() {
|
||||
match std::str::from_utf8(input) {
|
||||
Ok(parsed_str) => {
|
||||
append_escaped_str(&mut result, parsed_str);
|
||||
// The entire remaining input could be parsed, so we are done.
|
||||
break;
|
||||
}
|
||||
Err(e) => {
|
||||
let (valid, after_valid) = input.split_at(e.valid_up_to());
|
||||
// SAFETY: The previous `str::from_utf8` call established that the prefix `valid`
|
||||
// is valid UTF-8. This prefix may be empty.
|
||||
let parsed_str = unsafe { std::str::from_utf8_unchecked(valid) };
|
||||
append_escaped_str(&mut result, parsed_str);
|
||||
// The length of the prefix of `after_valid` which is invalid UTF-8.
|
||||
// The remaining bytes of `input` (if any) will be parsed in subsequent iterations
|
||||
// of the loop, starting from the first byte that starts a valid UTF-8-encoded codepoint.
|
||||
// `error_len` can return `None`, if it sees a byte sequence that could be the
|
||||
// prefix of a valid code-point encoding at the end of the byte slice.
|
||||
// This is useful when the input is chunked, but we don't do that, so in this case
|
||||
// we use our custom encoding for all remaining bytes (at most 3).
|
||||
let error_len = e.error_len().unwrap_or(after_valid.len());
|
||||
for byte in &after_valid[..error_len] {
|
||||
result.push(encode_byte_to_char(*byte));
|
||||
}
|
||||
input = &after_valid[error_len..];
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Use this rather than [`WString::from_str`] when the input could contain PUA bytes we use to
|
||||
/// encode non-UTF-8 bytes. Otherwise, when decoding the resulting [`WString`], the PUA bytes in
|
||||
/// the input would be converted to non-UTF-8 bytes.
|
||||
pub fn str2wcstring<S: AsRef<str>>(input: S) -> WString {
|
||||
bytes2wcstring(input.as_ref().as_bytes())
|
||||
}
|
||||
|
||||
pub fn cstr2wcstring<C: AsRef<CStr>>(input: C) -> WString {
|
||||
bytes2wcstring(input.as_ref().to_bytes())
|
||||
}
|
||||
|
||||
pub fn osstr2wcstring<O: AsRef<OsStr>>(input: O) -> WString {
|
||||
bytes2wcstring(input.as_ref().as_bytes())
|
||||
}
|
||||
|
||||
/// # SAFETY
|
||||
///
|
||||
/// `input` must point to a valid NUL-terminated string.
|
||||
pub unsafe fn charptr2wcstring(input: *const libc::c_char) -> WString {
|
||||
let input: &[u8] = unsafe { CStr::from_ptr(input).to_bytes() };
|
||||
bytes2wcstring(input)
|
||||
}
|
||||
|
||||
/// Finds `needle` in a `haystack` and returns the index of the first matching element, if any.
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -6,6 +6,8 @@ edition.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anstyle.workspace = true
|
||||
clap.workspace = true
|
||||
fish-build-helper.workspace = true
|
||||
fish-tempfile.workspace = true
|
||||
walkdir.workspace = true
|
||||
|
||||
170
crates/xtask/src/format.rs
Normal file
170
crates/xtask/src/format.rs
Normal file
@@ -0,0 +1,170 @@
|
||||
use anstyle::{AnsiColor, Style};
|
||||
use clap::Args;
|
||||
use std::{
|
||||
io::{ErrorKind, Write},
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
use crate::files_with_extension;
|
||||
|
||||
const GREEN: Style = AnsiColor::Green.on_default();
|
||||
const YELLOW: Style = AnsiColor::Yellow.on_default();
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct FormatArgs {
|
||||
/// Consider all eligible files.
|
||||
#[arg(long)]
|
||||
all: bool,
|
||||
/// Report files which are not formatted as expected, without modifying any files.
|
||||
#[arg(long)]
|
||||
check: bool,
|
||||
/// Format files even if uncommitted changes are detected.
|
||||
#[arg(long)]
|
||||
force: bool,
|
||||
paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
pub fn format(args: FormatArgs) {
|
||||
if !args.all && args.paths.is_empty() {
|
||||
println!(
|
||||
"{YELLOW}warning: No paths specified. Nothing to do. Use the \"--all\" flag to consider all eligible files.{YELLOW:#}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if !args.force && !args.check {
|
||||
match Command::new("git")
|
||||
.args(["status", "--porcelain", "--short", "--untracked-files=all"])
|
||||
.output()
|
||||
{
|
||||
Ok(output) => {
|
||||
if !output.stdout.is_empty() {
|
||||
std::io::stdout().write_all(&output.stdout).unwrap();
|
||||
print!(
|
||||
"You have uncommitted changes (listed above). Are you sure you want to format? (y/N): "
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
let mut response = String::new();
|
||||
std::io::stdin().read_line(&mut response).unwrap();
|
||||
if response.trim_end() != "y" {
|
||||
println!("Exiting without formatting.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == ErrorKind::NotFound {
|
||||
println!(
|
||||
"{YELLOW}warning: Did not find git, will proceed without checking for unstaged changes.{YELLOW:#}"
|
||||
)
|
||||
} else {
|
||||
fail!("Failed to run git status:\n{e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
format_fish(&args);
|
||||
format_python(&args);
|
||||
format_rust(&args);
|
||||
}
|
||||
|
||||
fn run_formatter(formatter: &mut Command, name: &str) {
|
||||
println!("=== Running {GREEN}{name}{GREEN:#}");
|
||||
match formatter.status() {
|
||||
Ok(exit_status) => {
|
||||
if !exit_status.success() {
|
||||
fail!("{name:?}: Files are not formatted correctly.");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
eprintln!(
|
||||
"{YELLOW}Formatter not found: {name:?}. Skipping associated files.{YELLOW:#}"
|
||||
);
|
||||
} else {
|
||||
fail!("Error occurred while running {name:?}:\n{e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn format_fish(args: &FormatArgs) {
|
||||
let mut fish_paths = files_with_extension(&args.paths, "fish");
|
||||
if args.all {
|
||||
let workspace_root = fish_build_helper::workspace_root();
|
||||
let fish_formatting_dirs = ["benchmarks", "build_tools", "etc", "share"];
|
||||
fish_paths.extend(files_with_extension(
|
||||
fish_formatting_dirs
|
||||
.iter()
|
||||
.map(|dir_name| workspace_root.join(dir_name)),
|
||||
"fish",
|
||||
));
|
||||
};
|
||||
if fish_paths.is_empty() {
|
||||
return;
|
||||
}
|
||||
// TODO: make `fish_indent` available as a Rust library function, to avoid needing a
|
||||
// `fish_indent` binary in `$PATH`.
|
||||
let mut formatter = Command::new("fish_indent");
|
||||
if args.check {
|
||||
formatter.arg("--check");
|
||||
} else {
|
||||
formatter.arg("-w");
|
||||
}
|
||||
formatter.arg("--");
|
||||
formatter.args(fish_paths);
|
||||
run_formatter(&mut formatter, "fish_indent");
|
||||
}
|
||||
|
||||
fn format_python(args: &FormatArgs) {
|
||||
let mut formatter = Command::new("ruff");
|
||||
formatter.arg("format");
|
||||
if args.check {
|
||||
formatter.arg("--check");
|
||||
}
|
||||
let mut python_files = files_with_extension(&args.paths, "py");
|
||||
|
||||
if args.all {
|
||||
python_files.push(fish_build_helper::workspace_root().to_owned());
|
||||
};
|
||||
if python_files.is_empty() {
|
||||
return;
|
||||
}
|
||||
formatter.args(python_files);
|
||||
run_formatter(&mut formatter, "ruff format");
|
||||
}
|
||||
|
||||
fn format_rust(args: &FormatArgs) {
|
||||
let rustfmt_status = Command::new("cargo")
|
||||
.arg("fmt")
|
||||
.arg("--version")
|
||||
.stdout(Stdio::null())
|
||||
.status()
|
||||
.unwrap();
|
||||
if !rustfmt_status.success() {
|
||||
eprintln!(
|
||||
"{YELLOW}Please install \"rustfmt\" to format Rust, e.g. via:\n\
|
||||
rustup component add rustfmt{YELLOW:#}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if args.all {
|
||||
let mut formatter = Command::new("cargo");
|
||||
formatter.arg("fmt");
|
||||
formatter.arg("--all");
|
||||
if args.check {
|
||||
formatter.arg("--check");
|
||||
}
|
||||
run_formatter(&mut formatter, "cargo fmt");
|
||||
}
|
||||
let rust_files = files_with_extension(&args.paths, "rs");
|
||||
if !rust_files.is_empty() {
|
||||
let mut formatter = Command::new("rustfmt");
|
||||
if args.check {
|
||||
formatter.arg("--check");
|
||||
formatter.arg("--files-with-diff");
|
||||
}
|
||||
formatter.args(rust_files);
|
||||
run_formatter(&mut formatter, "rustfmt");
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,34 @@
|
||||
use std::{ffi::OsStr, process::Command};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
macro_rules! fail {
|
||||
($($arg:tt)+) => {{
|
||||
eprintln!($($arg)+);
|
||||
std::process::exit(1);
|
||||
}}
|
||||
}
|
||||
|
||||
pub mod format;
|
||||
|
||||
pub trait CommandExt {
|
||||
fn run_or_panic(&mut self);
|
||||
fn run_or_fail(&mut self);
|
||||
}
|
||||
|
||||
impl CommandExt for Command {
|
||||
fn run_or_panic(&mut self) {
|
||||
fn run_or_fail(&mut self) {
|
||||
match self.status() {
|
||||
Ok(exit_status) => {
|
||||
if !exit_status.success() {
|
||||
panic!("Command did not run successfully: {:?}", self.get_program())
|
||||
fail!("Command did not run successfully: {:?}", self.get_program())
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
panic!("Failed to run command: {err}");
|
||||
fail!("Failed to run command: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,5 +39,32 @@ pub fn cargo<I, S>(cargo_args: I)
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
Command::new(env!("CARGO")).args(cargo_args).run_or_panic();
|
||||
Command::new(env!("CARGO")).args(cargo_args).run_or_fail();
|
||||
}
|
||||
|
||||
fn get_matching_files<P: AsRef<Path>, I: IntoIterator<Item = P>, M: Fn(&Path) -> bool>(
|
||||
all_paths: I,
|
||||
matcher: M,
|
||||
) -> Vec<PathBuf> {
|
||||
all_paths
|
||||
.into_iter()
|
||||
.flat_map(WalkDir::new)
|
||||
.filter_map(|res| {
|
||||
let entry = res.unwrap();
|
||||
let path = entry.path();
|
||||
if entry.file_type().is_file() && matcher(path) {
|
||||
Some(path.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn files_with_extension<P: AsRef<Path>, I: IntoIterator<Item = P>>(
|
||||
all_paths: I,
|
||||
extension: &str,
|
||||
) -> Vec<PathBuf> {
|
||||
let matcher = |p: &Path| p.extension().is_some_and(|e| e == extension);
|
||||
get_matching_files(all_paths, matcher)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
use fish_build_helper::as_os_strs;
|
||||
use std::{path::PathBuf, process::Command};
|
||||
use xtask::{CommandExt, cargo};
|
||||
use xtask::{CommandExt as _, cargo, format::FormatArgs};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(
|
||||
@@ -18,6 +18,8 @@ struct Cli {
|
||||
enum Task {
|
||||
/// Run various checks on the repo.
|
||||
Check,
|
||||
/// Format files or check if they are correctly formatted.
|
||||
Format(FormatArgs),
|
||||
/// Build HTML docs
|
||||
HtmlDocs {
|
||||
/// Path to a fish_indent executable. If none is specified, fish_indent will be built.
|
||||
@@ -32,6 +34,7 @@ fn main() {
|
||||
let cli = Cli::parse();
|
||||
match cli.task {
|
||||
Task::Check => run_checks(),
|
||||
Task::Format(format_args) => xtask::format::format(format_args),
|
||||
Task::HtmlDocs { fish_indent } => build_html_docs(fish_indent),
|
||||
Task::ManPages => cargo(["build", "--package", "fish-build-man-pages"]),
|
||||
}
|
||||
@@ -40,7 +43,7 @@ fn main() {
|
||||
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();
|
||||
Command::new(check_script).run_or_fail();
|
||||
}
|
||||
|
||||
fn build_html_docs(fish_indent: Option<PathBuf>) {
|
||||
@@ -91,5 +94,5 @@ fn build_html_docs(fish_indent: Option<PathBuf>) {
|
||||
Command::new(option_env!("FISH_SPHINX").unwrap_or("sphinx-build"))
|
||||
.env("PATH", new_path)
|
||||
.args(args)
|
||||
.run_or_panic();
|
||||
.run_or_fail();
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ The following ``argparse`` options are available. They must appear before all *O
|
||||
In contrast, if the known option comes first (and does not take any arguments), the known option will be recognised (e.g. ``argparse --move-unknown h -- -ho`` *will* set ``$_flag_h`` to ``-h``)
|
||||
|
||||
**-i** or **--ignore-unknown**
|
||||
Deprecated. This is like **--move-unknown**, except that unknown options and their arguments are kept in ``$argv`` and not moved to ``$argv_opts``. Unlike **--move-unknown**, this option makes it impossible to distinguish between an unknown option and non-option argument that starts with a ``-`` (since any ``--`` seperator in ``$argv`` will be removed).
|
||||
Deprecated. This is like **--move-unknown**, except that unknown options and their arguments are kept in ``$argv`` and not moved to ``$argv_opts``. Unlike **--move-unknown**, this option makes it impossible to distinguish between an unknown option and non-option argument that starts with a ``-`` (since any ``--`` separator in ``$argv`` will be removed).
|
||||
|
||||
**-S** or **--strict-longopts**
|
||||
This makes the parsing of long options more strict. In particular, *without* this flag, if ``long`` is a known long option flag, ``--long`` and ``--long=<value>`` can be abbreviated as:
|
||||
@@ -251,7 +251,7 @@ Some *OPTION_SPEC* examples:
|
||||
|
||||
- ``n/name=?`` means that both ``-n`` and ``--name`` are valid. It accepts an optional value and can be used at most once. If the flag is seen then ``_flag_n`` and ``_flag_name`` will be set with the value associated with the flag if one was provided else it will be set with no values.
|
||||
|
||||
- ``n/name=*`` is similar, but the flag can be used more than once. If the flag is seen then ``_flag_n`` and ``_flag_name`` will be set with the values associated with each occurence. Each value will be the value given to the option, or the empty string if no value was given.
|
||||
- ``n/name=*`` is similar, but the flag can be used more than once. If the flag is seen then ``_flag_n`` and ``_flag_name`` will be set with the values associated with each occurrence. Each value will be the value given to the option, or the empty string if no value was given.
|
||||
|
||||
- ``name=+`` means that only ``--name`` is valid. It requires a value and can be used more than once. If the flag is seen then ``_flag_name`` will be set with the values associated with each occurrence.
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ 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.
|
||||
@@ -401,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.
|
||||
|
||||
|
||||
@@ -34,6 +34,6 @@ A simple prompt that is a simplified version of the default debugging prompt::
|
||||
set -l function (status current-function)
|
||||
set -l line (status current-line-number)
|
||||
set -l prompt "$function:$line >"
|
||||
echo -ns (set_color $fish_color_status) "BP $prompt" (set_color normal) ' '
|
||||
echo -ns (set_color $fish_color_status) "BP $prompt" (set_color --reset) ' '
|
||||
end
|
||||
|
||||
|
||||
@@ -85,10 +85,10 @@ The format looks like this:
|
||||
fish_color_command 5c5cff
|
||||
|
||||
[unknown]
|
||||
fish_color_normal normal
|
||||
fish_color_normal --reset
|
||||
fish_color_autosuggestion brblack
|
||||
fish_color_cancel -r
|
||||
fish_color_command normal
|
||||
fish_color_command --reset
|
||||
|
||||
The comments provide name and background color to the web config tool.
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ When an interactive fish starts, it executes fish_greeting and displays its outp
|
||||
|
||||
The default fish_greeting is a function that prints a variable of the same name (``$fish_greeting``), so you can also just change that if you just want to change the text.
|
||||
|
||||
If :envvar:`SHELL_WELCOME` is set, it is displayed after the greeting. This is a standard environment variable that may be set by tools like systemd's ``run0`` to display session information.
|
||||
|
||||
While you could also just put ``echo`` calls into config.fish, fish_greeting takes care of only being used in interactive shells, so it won't be used e.g. with ``scp`` (which executes a shell), which prevents some errors.
|
||||
|
||||
Example
|
||||
@@ -39,5 +41,5 @@ A simple greeting:
|
||||
|
||||
function fish_greeting
|
||||
echo Hello friend!
|
||||
echo The time is (set_color yellow)(date +%T)(set_color normal) and this machine is called $hostname
|
||||
echo The time is (set_color yellow)(date +%T)(set_color --reset) and this machine is called $hostname
|
||||
end
|
||||
|
||||
@@ -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``, ``visual``, or ``operator``.
|
||||
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,14 +55,14 @@ Example
|
||||
case visual
|
||||
set_color --bold brmagenta
|
||||
echo 'V'
|
||||
case operator
|
||||
case operator f F t T
|
||||
set_color --bold cyan
|
||||
echo 'N'
|
||||
case '*'
|
||||
set_color --bold red
|
||||
echo '?'
|
||||
end
|
||||
set_color normal
|
||||
set_color --reset
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ The exit status of commands within ``fish_prompt`` will not modify the value of
|
||||
|
||||
If :envvar:`fish_transient_prompt` is set to 1, ``fish_prompt --final-rendering`` is run before executing the commandline.
|
||||
|
||||
If :envvar:`SHELL_PROMPT_PREFIX` or :envvar:`SHELL_PROMPT_SUFFIX` are set, they are automatically prepended and appended to the left prompt. This applies to all prompts regardless of whether ``fish_prompt`` has been customized.
|
||||
|
||||
``fish`` ships with a number of example prompts that can be chosen with the ``fish_config`` command.
|
||||
|
||||
|
||||
@@ -41,7 +43,7 @@ A simple prompt:
|
||||
# $USER and $hostname are set by fish, so you can just use them
|
||||
# instead of using `whoami` and `hostname`
|
||||
printf '%s@%s %s%s%s > ' $USER $hostname \
|
||||
(set_color $fish_color_cwd) (prompt_pwd) (set_color normal)
|
||||
(set_color $fish_color_cwd) (prompt_pwd) (set_color --reset)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ Description
|
||||
|
||||
``if`` will execute the command ``CONDITION``. If the condition's exit status is 0, the commands ``COMMANDS_TRUE`` will execute. If the exit status is not 0 and :doc:`else <else>` is given, ``COMMANDS_FALSE`` will be executed.
|
||||
|
||||
You can use :doc:`and <and>` or :doc:`or <or>` in the condition. See the second example below.
|
||||
You can use :doc:`not <not>`, :doc:`and <and>` or :doc:`or <or>` in the condition. See the second example below.
|
||||
|
||||
The exit status of the last foreground command to exit can always be accessed using the :ref:`$status <variables-status>` variable.
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ The following options control the interactive mode:
|
||||
Masks characters written to the terminal, replacing them with asterisks. This is useful for reading things like passwords or other sensitive information.
|
||||
|
||||
**-p** or **--prompt** *PROMPT_CMD*
|
||||
Uses the output of the shell command *PROMPT_CMD* as the prompt for the interactive mode. The default prompt command is ``set_color green; echo -n read; set_color normal; echo -n "> "``
|
||||
Uses the output of the shell command *PROMPT_CMD* as the prompt for the interactive mode. The default prompt command is ``set_color green; echo -n read; set_color --reset; echo -n "> "``
|
||||
|
||||
**-P** or **--prompt-str** *PROMPT_STR*
|
||||
Uses the literal *PROMPT_STR* as the prompt for the interactive mode.
|
||||
|
||||
@@ -6,16 +6,17 @@ Synopsis
|
||||
|
||||
.. synopsis::
|
||||
|
||||
set
|
||||
set (-f | --function) (-l | --local) (-g | --global) (-U | --universal) [--no-event]
|
||||
set [-Uflg] NAME [VALUE ...]
|
||||
set [-Uflg] NAME[[INDEX ...]] [VALUE ...]
|
||||
set (-x | --export) (-u | --unexport) [-Uflg] NAME [VALUE ...]
|
||||
set (-a | --append) (-p | --prepend) [-Uflg] NAME VALUE ...
|
||||
set (-e | --erase) [-Uflg] [-xu] [NAME][[INDEX]] ...]
|
||||
set (-q | --query) [-Uflg] [-xu] [NAME][[INDEX]] ...]
|
||||
set [(-f | --function) (-l | --local) (-g | --global) (-U | --universal)]
|
||||
[(-x | --export) (-u | --unexport)]
|
||||
set (-S | --show) (-L | --long) [NAME ...]
|
||||
|
||||
set [-Uflg] [-xu] [--no-event] NAME [VALUE ...]
|
||||
set [-Uflg] [--no-event] NAME[[INDEX ...]] [VALUE ...]
|
||||
set (-a | --append) (-p | --prepend) [-Uflg] [--no-event] NAME VALUE ...
|
||||
set (-e | --erase) [-Uflg] [--no-event] NAME[[INDEX]] ...
|
||||
|
||||
set (-q | --query) [-Uflg] [-xu] NAME[[INDEX]] ...
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
@@ -89,7 +90,7 @@ Further options:
|
||||
**-q** or **--query** *NAME*\[*INDEX*\]
|
||||
Test if the specified variable names are defined.
|
||||
If an *INDEX* is provided, check for items at that slot.
|
||||
With a given scope (like **--global**) or attribute (like **--exported** or **--path**) check only variables that match.
|
||||
With a given scope (like **--global**) or attribute (like **--export** or **--path**) check only variables that match.
|
||||
Does not output anything, but the shell status is set to the number of variables specified that were not defined, up to a maximum of 255.
|
||||
If no variable was given, it also returns 255.
|
||||
|
||||
|
||||
@@ -6,12 +6,15 @@ Synopsis
|
||||
|
||||
.. synopsis::
|
||||
|
||||
set_color [OPTIONS] VALUE
|
||||
set_color [OPTIONS] [VALUE]
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
``set_color`` is used to control the color and styling of text in the terminal. *VALUE* describes that styling. *VALUE* can be a reserved color name like **red** or an RGB color value given as 3 or 6 hexadecimal digits ("F27" or "FF2277"). A special keyword **normal** resets text formatting to terminal defaults.
|
||||
``set_color`` is used to control the color and styling of text in the terminal.
|
||||
*VALUE* describes that styling.
|
||||
*VALUE* can be a reserved color name like **red** or an RGB color value given as 3 or 6 hexadecimal digits ("F27" or "FF2277").
|
||||
A special keyword **normal** resets text formatting to terminal defaults, however it is not recommended and the **--reset** option is preferred as it is less confusing and more future-proof.
|
||||
|
||||
Valid colors include:
|
||||
|
||||
@@ -33,6 +36,11 @@ However if :envvar:`fish_term256` is set to 0, fish prefers the first named colo
|
||||
|
||||
The following options are available:
|
||||
|
||||
**-f** or **--foreground** *COLOR*
|
||||
Sets the foreground color.
|
||||
This is equivalent to calling ``set_color COLOR`` with the exception that the keyword **normal** will only reset the foreground color to its default, instead of all colors and modes.
|
||||
It cannot be used with *VALUE* or **--print-colors**.
|
||||
|
||||
**-b** or **--background** *COLOR*
|
||||
Sets the background color.
|
||||
|
||||
@@ -41,6 +49,7 @@ The following options are available:
|
||||
|
||||
**-c** or **--print-colors**
|
||||
Prints the given colors or a colored list of the 16 named colors.
|
||||
It cannot be used with **--foreground**.
|
||||
|
||||
**-o** or **--bold**
|
||||
Sets bold mode.
|
||||
@@ -48,17 +57,21 @@ The following options are available:
|
||||
**-d** or **--dim**
|
||||
Sets dim mode.
|
||||
|
||||
**-i** or **--italics**
|
||||
Sets italics mode.
|
||||
**-i** or **--italics**, or **-iSTATE** or **--italics=STATE**
|
||||
Sets italics mode. The state can be **on** (default), or **off**.
|
||||
|
||||
**-r** or **--reverse**
|
||||
Sets reverse mode.
|
||||
**-r** or **--reverse**, or **-iSTATE** or **--reverse=STATE**
|
||||
Sets reverse mode. The state can be **on** (default), or **off**.
|
||||
|
||||
**-s** or **--strikethrough**
|
||||
Sets strikethrough mode.
|
||||
**-s** or **--strikethrough**, or **-sSTATE** or **--strikethrough=STATE**
|
||||
Sets strikethrough mode. The state can be **on** (default), or **off**.
|
||||
|
||||
**-u** or **--underline**, or **-uSTYLE** or **--underline=STYLE**
|
||||
Set the underline mode; supported styles are **single** (default), **double**, **curly**, **dotted** and **dashed**.
|
||||
Set the underline mode; supported styles are **single** (default), **double**, **curly**, **dotted**, **dashed** and **off**.
|
||||
|
||||
**--reset**
|
||||
Reset the text formatting to the terminal defaults before applying the new colors and modes.
|
||||
This is equivalent to calling ``set_color normal`` except that it is possible to set the foreground color in the same call (e.g. ``set_color --reset green``)
|
||||
|
||||
**--theme=THEME**
|
||||
Ignored.
|
||||
@@ -75,10 +88,12 @@ The following options are available:
|
||||
Notes
|
||||
-----
|
||||
|
||||
1. Using **set_color normal** will reset all colors and modes to the terminal's default.
|
||||
2. Setting the background color only affects subsequently written characters. Fish provides no way to set the background color for the entire terminal window. Configuring the window background color (and other attributes such as its opacity) has to be done using whatever mechanisms the terminal provides. Look for a config option.
|
||||
3. Some terminals use the ``--bold`` escape sequence to switch to a brighter color set rather than increasing the weight of text.
|
||||
4. ``set_color`` works by printing sequences of characters to standard output. If used in command substitution or a pipe, these characters will also be captured. This may or may not be desirable. Checking the exit status of ``isatty stdout`` before using ``set_color`` can be useful to decide not to colorize output in a script.
|
||||
1. Using ``set_color normal`` will reset all colors and modes to the terminal's default.
|
||||
2. In contrast, ``set_color --foreground normal`` will only reset the foreground color and leave all the other colors and modes unchanged.
|
||||
3. Because of the risk of confusion, ``set_color --reset`` is recommended over ``set_color normal``.
|
||||
4. Setting the background color only affects subsequently written characters. Fish provides no way to set the background color for the entire terminal window. Configuring the window background color (and other attributes such as its opacity) has to be done using whatever mechanisms the terminal provides. Look for a config option.
|
||||
5. Some terminals use the ``--bold`` escape sequence to switch to a brighter color set rather than increasing the weight of text.
|
||||
6. ``set_color`` works by printing sequences of characters to standard output. If used in command substitution or a pipe, these characters will also be captured. This may or may not be desirable. Checking the exit status of ``isatty stdout`` before using ``set_color`` can be useful to decide not to colorize output in a script.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
@@ -91,7 +91,7 @@ The prompt is the output of the ``fish_prompt`` function. Put it in ``~/.config/
|
||||
function fish_prompt
|
||||
set_color $fish_color_cwd
|
||||
echo -n (prompt_pwd)
|
||||
set_color normal
|
||||
set_color --reset
|
||||
echo -n ' > '
|
||||
end
|
||||
|
||||
@@ -329,7 +329,7 @@ Sometimes, there is disagreement on the width. There are numerous causes and fix
|
||||
- It is possible the character is too new for your system to know - in this case you need to refrain from using it.
|
||||
- Fish or your terminal might not know about the character or handle it wrong - in this case fish or your terminal needs to be fixed, or you need to update to a fixed version.
|
||||
- The character has an "ambiguous" width and fish thinks that means a width of X while your terminal thinks it's Y. In this case you either need to change your terminal's configuration or set $fish_ambiguous_width to the correct value.
|
||||
- The character is an emoji and the host system only supports Unicode 8, while you are running the terminal on a system that uses Unicode >= 9. In this case set $fish_emoji_width to 2.
|
||||
- The character is an emoji and your system only supports Unicode 8. In this case set $fish_emoji_width to 1.
|
||||
|
||||
This also means that a few things are unsupportable:
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ and a rough fish equivalent::
|
||||
|
||||
echo -s (prompt_hostname) \
|
||||
(set_color blue) (prompt_pwd) \
|
||||
(set_color yellow) $prompt_symbol (set_color normal)
|
||||
(set_color yellow) $prompt_symbol (set_color --reset)
|
||||
end
|
||||
|
||||
This shows a few differences:
|
||||
|
||||
@@ -54,7 +54,7 @@ It also provides a large number of program specific scripted completions. Most o
|
||||
|
||||
You can also write your own completions or install some you got from someone else. For that, see :doc:`Writing your own completions <completions>`.
|
||||
|
||||
Completion scripts are loaded on demand, like :ref:`functions are <syntax-function-autoloading>`. The difference is the ``$fish_complete_path`` :ref:`list <variables-lists>` is used instead of ``$fish_function_path``. Typically you can drop new completions in ~/.config/fish/completions/name-of-command.fish and fish will find them automatically.
|
||||
Completion scripts are loaded on demand, like :ref:`functions are <syntax-function-autoloading>`. The difference is the ``$fish_complete_path`` :ref:`list <variables-lists>` is used instead of ``$fish_function_path``. Typically you can drop new completions in ``~/.config/fish/completions/<name-of-command>.fish`` and fish will find them automatically.
|
||||
|
||||
.. _syntax-highlighting:
|
||||
|
||||
@@ -105,6 +105,7 @@ Syntax highlighting variables
|
||||
|
||||
The colors used by fish for syntax highlighting can be configured by changing the values of various variables. The value of these variables can be one of the colors accepted by the :doc:`set_color <cmds/set_color>` command.
|
||||
Options accepted by ``set_color`` like
|
||||
``--foreground=``,
|
||||
``--background=``,
|
||||
``--bold``,
|
||||
``--dim``,
|
||||
|
||||
@@ -235,7 +235,7 @@ It is possible to pipe a different output file descriptor by prepending its FD n
|
||||
|
||||
will attempt to build ``fish``, and any errors will be shown using the ``less`` pager. [#]_
|
||||
|
||||
As a convenience, the pipe ``&|`` redirects both stdout and stderr to the same process. This is different from bash, which uses ``|&``.
|
||||
As a convenience, the pipe ``&|`` (as well as the ``|&`` alias which is also supported by Bash) both redirect stdout and stderr to the same process.
|
||||
|
||||
.. [#] A "pager" here is a program that takes output and "paginates" it. ``less`` doesn't just do pages, it allows arbitrary scrolling (even back!).
|
||||
|
||||
@@ -1573,7 +1573,7 @@ You can change the settings of fish by changing the values of certain variables.
|
||||
|
||||
.. envvar:: fish_emoji_width
|
||||
|
||||
controls whether fish assumes emoji render as 2 cells or 1 cell wide. This is necessary because the correct value changed from 1 to 2 in Unicode 9, and some terminals may not be aware. Set this if you see graphical glitching related to emoji (or other "special" characters). It should usually be auto-detected.
|
||||
controls whether fish assumes emoji render as 2 cells or 1 cell wide. This is necessary because the correct value changed from 1 to 2 in Unicode 9, and some terminals may not be aware. Set this if you see graphical glitching related to emoji (or other "special" characters). It defaults to 2.
|
||||
|
||||
.. envvar:: fish_autosuggestion_enabled
|
||||
|
||||
@@ -1646,6 +1646,18 @@ You can change the settings of fish by changing the values of certain variables.
|
||||
|
||||
the current file creation mask. The preferred way to change the umask variable is through the :doc:`umask <cmds/umask>` function. An attempt to set umask to an invalid value will always fail.
|
||||
|
||||
.. envvar:: SHELL_PROMPT_PREFIX
|
||||
|
||||
if set, this string is automatically prepended to the left prompt. This is a standard environment variable that may be set by tools like systemd's ``run0`` to indicate special shell sessions.
|
||||
|
||||
.. envvar:: SHELL_PROMPT_SUFFIX
|
||||
|
||||
if set, this string is automatically appended to the left prompt. This is a standard environment variable that may be set by tools like systemd's ``run0`` to indicate special shell sessions.
|
||||
|
||||
.. envvar:: SHELL_WELCOME
|
||||
|
||||
if set, this string is displayed when an interactive shell starts, after the greeting. This is a standard environment variable that may be set by tools like systemd's ``run0`` to display session information.
|
||||
|
||||
.. envvar:: BROWSER
|
||||
|
||||
your preferred web browser. If this variable is set, fish will use the specified browser instead of the system default browser to display the fish documentation.
|
||||
@@ -2043,8 +2055,8 @@ Here is what they mean:
|
||||
- ``ampersand-nobg-in-token`` was introduced in fish 3.4 (and made the default in 3.5). It makes it so a ``&`` is no longer interpreted as the backgrounding operator in the middle of a token, so dealing with URLs becomes easier. Either put spaces or a semicolon after the ``&``. This is recommended formatting anyway, and ``fish_indent`` will have done it for you already.
|
||||
- ``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.
|
||||
- ``mark-prompt`` makes fish report to the terminal the beginning and end of both shell prompts and command output.
|
||||
- ``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``.
|
||||
|
||||
@@ -19,6 +19,8 @@ Unlike other shells, fish's prompt is built by running a function - :doc:`fish_p
|
||||
|
||||
These functions are run, and whatever they print is displayed as the prompt (minus one trailing newline).
|
||||
|
||||
If the :envvar:`SHELL_PROMPT_PREFIX` or :envvar:`SHELL_PROMPT_SUFFIX` environment variables are set, they are automatically prepended and appended to the left prompt.
|
||||
|
||||
Here, we will just be writing a simple fish_prompt.
|
||||
|
||||
Our first prompt
|
||||
@@ -67,10 +69,10 @@ Fortunately, fish offers the :doc:`set_color <cmds/set_color>` command, so you c
|
||||
So, taking our previous prompt and adding some color::
|
||||
|
||||
function fish_prompt
|
||||
string join '' -- (set_color green) $PWD (set_color normal) '>'
|
||||
string join '' -- (set_color green) $PWD (set_color --reset) '>'
|
||||
end
|
||||
|
||||
A "normal" color tells the terminal to go back to its normal formatting options.
|
||||
"--reset" tells the terminal to go back to its default formatting options.
|
||||
|
||||
``set_color`` works by producing an escape sequence, which is a special piece of text that terminals
|
||||
interpret as instructions - for example, to change color. So ``set_color red`` produces the same
|
||||
@@ -86,13 +88,13 @@ Shortening the working directory
|
||||
This is fine, but our :envvar:`PWD` can be a bit long, and we are typically only interested in the last few directories. We can shorten this with the :doc:`prompt_pwd <cmds/prompt_pwd>` helper that will give us a shortened working directory::
|
||||
|
||||
function fish_prompt
|
||||
string join '' -- (set_color green) (prompt_pwd) (set_color normal) '>'
|
||||
string join '' -- (set_color green) (prompt_pwd) (set_color --reset) '>'
|
||||
end
|
||||
|
||||
``prompt_pwd`` takes options to control how much to shorten. For instance, if we want to display the last two directories, we'd use ``prompt_pwd --full-length-dirs 2``::
|
||||
|
||||
function fish_prompt
|
||||
string join '' -- (set_color green) (prompt_pwd --full-length-dirs 2) (set_color normal) '>'
|
||||
string join '' -- (set_color green) (prompt_pwd --full-length-dirs 2) (set_color --reset) '>'
|
||||
end
|
||||
|
||||
With a current directory of "/home/tutorial/Music/Lena Raine/Oneknowing", this would print
|
||||
@@ -119,12 +121,12 @@ And after that, you can set a string if it is not zero::
|
||||
# Prompt status only if it's not 0
|
||||
set -l stat
|
||||
if test $last_status -ne 0
|
||||
set stat (set_color red)"[$last_status]"(set_color normal)
|
||||
set stat (set_color red)"[$last_status]"(set_color --reset)
|
||||
end
|
||||
|
||||
And to print it, we add it to our ``string join``::
|
||||
|
||||
string join '' -- (set_color green) (prompt_pwd) (set_color normal) $stat '>'
|
||||
string join '' -- (set_color green) (prompt_pwd) (set_color --reset) $stat '>'
|
||||
|
||||
If ``$last_status`` was 0, ``$stat`` is empty, and so it will simply disappear.
|
||||
|
||||
@@ -135,10 +137,10 @@ So our entire prompt is now::
|
||||
# Prompt status only if it's not 0
|
||||
set -l stat
|
||||
if test $last_status -ne 0
|
||||
set stat (set_color red)"[$last_status]"(set_color normal)
|
||||
set stat (set_color red)"[$last_status]"(set_color --reset)
|
||||
end
|
||||
|
||||
string join '' -- (set_color green) (prompt_pwd) (set_color normal) $stat '>'
|
||||
string join '' -- (set_color green) (prompt_pwd) (set_color --reset) $stat '>'
|
||||
end
|
||||
|
||||
And it looks like:
|
||||
@@ -176,11 +178,11 @@ So you can use it to declutter your old prompts. For example if you want to see
|
||||
set pwd (prompt_pwd)
|
||||
# Prompt status only if it's not 0
|
||||
if test $last_status -ne 0
|
||||
set stat (set_color red)"[$last_status]"(set_color normal)
|
||||
set stat (set_color red)"[$last_status]"(set_color --reset)
|
||||
end
|
||||
end
|
||||
|
||||
string join '' -- (set_color green) $pwd (set_color normal) $stat '>'
|
||||
string join '' -- (set_color green) $pwd (set_color --reset) $stat '>'
|
||||
end
|
||||
|
||||
Now running two commands in the same directory could result in this screen:
|
||||
|
||||
@@ -155,6 +155,9 @@ Optional Commands
|
||||
* - ``\e[48;2; Ps ; Ps ; Ps m``
|
||||
-
|
||||
- Select background color from 24-bit RGB colors.
|
||||
* - ``\e[39m``
|
||||
-
|
||||
- Reset foreground color to the terminal's default.
|
||||
* - ``\e[49m``
|
||||
-
|
||||
- Reset background color to the terminal's default.
|
||||
|
||||
@@ -633,7 +633,7 @@ Multiple lines are OK. Colors can be set via :doc:`set_color <cmds/set_color>` b
|
||||
set_color purple
|
||||
date "+%m/%d/%y"
|
||||
set_color FF0000
|
||||
echo (pwd) '>' (set_color normal)
|
||||
echo (pwd) '>' (set_color --reset)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -37,8 +37,8 @@ RUN adduser \
|
||||
fishuser
|
||||
|
||||
RUN mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
|
||||
USER fishuser
|
||||
WORKDIR /home/fishuser
|
||||
|
||||
@@ -2,24 +2,24 @@ FROM fedora:latest
|
||||
LABEL org.opencontainers.image.source=https://github.com/fish-shell/fish-shell
|
||||
|
||||
RUN dnf install --assumeyes \
|
||||
diffutils \
|
||||
gcc-c++ \
|
||||
git-core \
|
||||
pcre2-devel \
|
||||
python3 \
|
||||
python3-pip \
|
||||
openssl \
|
||||
procps \
|
||||
sudo && \
|
||||
diffutils \
|
||||
gcc-c++ \
|
||||
git-core \
|
||||
pcre2-devel \
|
||||
python3 \
|
||||
python3-pip \
|
||||
openssl \
|
||||
procps \
|
||||
sudo && \
|
||||
dnf clean all
|
||||
|
||||
RUN pip3 install pexpect
|
||||
|
||||
RUN groupadd -g 1000 fishuser \
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser -G wheel \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser -G wheel \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
|
||||
USER fishuser
|
||||
WORKDIR /home/fishuser
|
||||
|
||||
@@ -5,28 +5,28 @@ ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
|
||||
RUN zypper --non-interactive install \
|
||||
bash \
|
||||
diffutils \
|
||||
gcc-c++ \
|
||||
git-core \
|
||||
pcre2-devel \
|
||||
python311 \
|
||||
python311-pip \
|
||||
python311-pexpect \
|
||||
openssl \
|
||||
procps \
|
||||
tmux \
|
||||
sudo \
|
||||
rust \
|
||||
cargo
|
||||
bash \
|
||||
diffutils \
|
||||
gcc-c++ \
|
||||
git-core \
|
||||
pcre2-devel \
|
||||
python311 \
|
||||
python311-pip \
|
||||
python311-pexpect \
|
||||
openssl \
|
||||
procps \
|
||||
tmux \
|
||||
sudo \
|
||||
rust \
|
||||
cargo
|
||||
|
||||
RUN usermod -p $(openssl passwd -1 fish) root
|
||||
|
||||
RUN groupadd -g 1000 fishuser \
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
|
||||
USER fishuser
|
||||
WORKDIR /home/fishuser
|
||||
|
||||
@@ -6,37 +6,37 @@ ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
cmake ninja-build \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
clang \
|
||||
curl \
|
||||
gettext \
|
||||
git \
|
||||
libpcre2-dev \
|
||||
locales \
|
||||
openssl \
|
||||
python3 \
|
||||
python3-pexpect \
|
||||
sudo \
|
||||
tmux \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& apt-get clean
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
cmake ninja-build \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
clang \
|
||||
curl \
|
||||
gettext \
|
||||
git \
|
||||
libpcre2-dev \
|
||||
locales \
|
||||
openssl \
|
||||
python3 \
|
||||
python3-pexpect \
|
||||
sudo \
|
||||
tmux \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& apt-get clean
|
||||
|
||||
RUN userdel ubuntu \
|
||||
&& groupadd -g 1000 fishuser \
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
||||
-G sudo \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:1000 /home/fishuser /fish-source
|
||||
&& groupadd -g 1000 fishuser \
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
||||
-G sudo \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:1000 /home/fishuser /fish-source
|
||||
|
||||
USER fishuser
|
||||
WORKDIR /home/fishuser
|
||||
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > /tmp/rustup.sh \
|
||||
&& sh /tmp/rustup.sh -y --no-modify-path
|
||||
&& sh /tmp/rustup.sh -y --no-modify-path
|
||||
ENV PATH=/home/fishuser/.cargo/bin:$PATH
|
||||
|
||||
COPY fish_run_tests.sh /
|
||||
|
||||
@@ -6,36 +6,36 @@ ENV LANG=C.UTF-8
|
||||
ENV LC_ALL=C.UTF-8
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
cmake ninja-build \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
clang \
|
||||
curl \
|
||||
gettext \
|
||||
git \
|
||||
libpcre2-dev \
|
||||
locales \
|
||||
openssl \
|
||||
python3 \
|
||||
python3-pexpect \
|
||||
sudo \
|
||||
tmux \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& apt-get clean
|
||||
&& apt-get -y install --no-install-recommends \
|
||||
cmake ninja-build \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
clang \
|
||||
curl \
|
||||
gettext \
|
||||
git \
|
||||
libpcre2-dev \
|
||||
locales \
|
||||
openssl \
|
||||
python3 \
|
||||
python3-pexpect \
|
||||
sudo \
|
||||
tmux \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& apt-get clean
|
||||
|
||||
RUN groupadd -g 1000 fishuser \
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
||||
&& adduser fishuser sudo \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
||||
&& adduser fishuser sudo \
|
||||
&& mkdir -p /home/fishuser/fish-build \
|
||||
&& mkdir /fish-source \
|
||||
&& chown -R fishuser:fishuser /home/fishuser /fish-source
|
||||
|
||||
USER fishuser
|
||||
WORKDIR /home/fishuser
|
||||
|
||||
RUN curl --proto '=https' --tlsv1.2 -fsS https://sh.rustup.rs > /tmp/rustup.sh \
|
||||
&& sh /tmp/rustup.sh -y --no-modify-path
|
||||
&& sh /tmp/rustup.sh -y --no-modify-path
|
||||
ENV PATH=/home/fishuser/.cargo/bin:$PATH
|
||||
|
||||
COPY fish_run_tests.sh /
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
80820
localization/po/es.po
Normal file
80820
localization/po/es.po
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
80823
localization/po/ja_JP.po
Normal file
80823
localization/po/ja_JP.po
Normal file
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
@@ -2,7 +2,7 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -3,12 +3,12 @@ name = "fish-shell"
|
||||
version = "0.0.0"
|
||||
# NOTE: versions for Python and Sphinx are specified for reproducibility.
|
||||
# Lower versions will work too.
|
||||
requires-python = ">=3.11" # updatecli.d/python.yml
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"sphinx>=9.0", # updatecli.d/python.yml
|
||||
"sphinx>=9.1", # updatecli.d/python.yml
|
||||
"sphinx-markdown-builder",
|
||||
]
|
||||
|
||||
|
||||
@@ -4,25 +4,31 @@ complete -c ansible-galaxy -s h -l help -d "Show help message"
|
||||
complete -c ansible-galaxy -n __fish_use_subcommand -s v -l verbose -d "Verbose mode (-vvv for more, -vvvv for connection debugging)"
|
||||
|
||||
# first subcommand
|
||||
complete -c ansible-galaxy -n __fish_use_subcommand -xa "collection\t'Manage a collection'
|
||||
role\t'Manage a role'"
|
||||
complete -c ansible-galaxy -n __fish_use_subcommand -xa "
|
||||
collection\t'Manage a collection'
|
||||
role\t'Manage a role'
|
||||
"
|
||||
|
||||
# second subcommand (for collection)
|
||||
complete -c ansible-galaxy -n '__fish_seen_subcommand_from collection' -a "download\t'Download collections as tarball'
|
||||
init\t'Initialize new collection with the base structure'
|
||||
build\t'Build collection artifact that can be published'
|
||||
publish\t'Publish collection artifact to Ansible Galaxy'
|
||||
install\t'Install collections'
|
||||
list\t'Show collections installed'
|
||||
verify\t'Compare checksums of local and remote collections'"
|
||||
complete -c ansible-galaxy -n '__fish_seen_subcommand_from collection' -a "
|
||||
download\t'Download collections as tarball'
|
||||
init\t'Initialize new collection with the base structure'
|
||||
build\t'Build collection artifact that can be published'
|
||||
publish\t'Publish collection artifact to Ansible Galaxy'
|
||||
install\t'Install collections'
|
||||
list\t'Show collections installed'
|
||||
verify\t'Compare checksums of local and remote collections'
|
||||
"
|
||||
|
||||
# second subcommand (for role)
|
||||
complete -c ansible-galaxy -n '__fish_seen_subcommand_from role' -a "init\t'Initialize new role with the base structure'
|
||||
remove\t'Delete roles from roles_path'
|
||||
delete\t'Removes the role from Galaxy'
|
||||
list\t'Show roles installed'
|
||||
search\t'Search the Galaxy database by keywords'
|
||||
import\t'Import role into a galaxy server'
|
||||
setup\t'Manage integration between Galaxy and the given source'
|
||||
info\t'View details about a role'
|
||||
install\t'Install roles'"
|
||||
complete -c ansible-galaxy -n '__fish_seen_subcommand_from role' -a "
|
||||
init\t'Initialize new role with the base structure'
|
||||
remove\t'Delete roles from roles_path'
|
||||
delete\t'Removes the role from Galaxy'
|
||||
list\t'Show roles installed'
|
||||
search\t'Search the Galaxy database by keywords'
|
||||
import\t'Import role into a galaxy server'
|
||||
setup\t'Manage integration between Galaxy and the given source'
|
||||
info\t'View details about a role'
|
||||
install\t'Install roles'
|
||||
"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## --- WRITTEN MANUALLY ---
|
||||
|
||||
function __fish_cargo
|
||||
cargo --color=never $argv
|
||||
RUSTUP_AUTO_INSTALL=0 cargo --color=never $argv
|
||||
end
|
||||
|
||||
set -l __fish_cargo_subcommands (__fish_cargo --list 2>&1 | string replace -rf '^\s+([^\s]+)\s*(.*)' '$1\t$2' | string escape)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
# This grep tries to match nonempty lines that do not start with hash
|
||||
complete -c chsh -s s -l shell -x -a "(string match -r '^[^#].*' < /etc/shells)" -d "Specify your login shell"
|
||||
complete -c chsh -s u -l help -d "Display help and exit "
|
||||
complete -c chsh -s u -l help -d "Display help and exit"
|
||||
complete -c chsh -s v -l version -d "Display version and exit"
|
||||
complete -x -c chsh -a "(__fish_complete_users)"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user