mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-09 08:51:14 -03:00
Compare commits
136 Commits
Integratio
...
Integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56bbdb3f39 | ||
|
|
ce4aa7669d | ||
|
|
af8d8d3d1b | ||
|
|
fecd0b4bf1 | ||
|
|
2d4a43302a | ||
|
|
5a4a913220 | ||
|
|
904ceba858 | ||
|
|
3c2adfbd4b | ||
|
|
9e4850b40a | ||
|
|
211b3f6670 | ||
|
|
d530e127f5 | ||
|
|
a6698098db | ||
|
|
f73b260a3a | ||
|
|
ffa7abd6ff | ||
|
|
b87ef689fa | ||
|
|
4fe70f6965 | ||
|
|
7001abca9f | ||
|
|
199316f1a3 | ||
|
|
78c9ab29cd | ||
|
|
91c9dbdd89 | ||
|
|
e737ad1f0f | ||
|
|
6f536c6304 | ||
|
|
a1b4b391b2 | ||
|
|
a27f615350 | ||
|
|
46ce8a1d2f | ||
|
|
03ccc88868 | ||
|
|
79cf4c3e0b | ||
|
|
701e1a3b02 | ||
|
|
b1ec703ceb | ||
|
|
553612f74a | ||
|
|
861decb003 | ||
|
|
3b8780aa6c | ||
|
|
7783505bee | ||
|
|
23c25ffe43 | ||
|
|
7619fa316c | ||
|
|
e2005c64b3 | ||
|
|
9ada3e6c16 | ||
|
|
bdba2c227d | ||
|
|
201882e72a | ||
|
|
1db0ff9f77 | ||
|
|
9258275fe6 | ||
|
|
6900b89c82 | ||
|
|
9c0086b7af | ||
|
|
e200abe39c | ||
|
|
0c8f1f4220 | ||
|
|
e593da1c2e | ||
|
|
6666c8f1cd | ||
|
|
3e61036911 | ||
|
|
f23a479b81 | ||
|
|
e274ff41d0 | ||
|
|
d2af306f3d | ||
|
|
0e7c7f1745 | ||
|
|
3fada80553 | ||
|
|
c7d4acbef8 | ||
|
|
e204a4c126 | ||
|
|
9af33802ec | ||
|
|
eecf0814a1 | ||
|
|
1ceebdf580 | ||
|
|
335f91babd | ||
|
|
ec66749369 | ||
|
|
6e9e33d81d | ||
|
|
f3ebc68d5d | ||
|
|
4be17bfefb | ||
|
|
6fd0025f38 | ||
|
|
052fc18db9 | ||
|
|
63a08e53e5 | ||
|
|
62ac23453e | ||
|
|
c052beb4dd | ||
|
|
e0cabacdaa | ||
|
|
59b9f57802 | ||
|
|
65fc2b539c | ||
|
|
3f1add9e21 | ||
|
|
68d2cafa6e | ||
|
|
b9d9e7edc6 | ||
|
|
6b17ec7dae | ||
|
|
08d796890a | ||
|
|
4f98ef36f6 | ||
|
|
a09c78491f | ||
|
|
02932d6b8c | ||
|
|
7cca98bda2 | ||
|
|
b77fc28692 | ||
|
|
3475531ef7 | ||
|
|
8222ed891b | ||
|
|
f787e6858c | ||
|
|
a014166795 | ||
|
|
f7e639504a | ||
|
|
028b60cad6 | ||
|
|
b11e22d905 | ||
|
|
33f8415785 | ||
|
|
5ccd155177 | ||
|
|
0f8d3a5174 | ||
|
|
c4a26cb2b1 | ||
|
|
7228cb15bf | ||
|
|
d5b46d6535 | ||
|
|
d4b4d44f14 | ||
|
|
b8cfd6d12b | ||
|
|
6fb22a4fd1 | ||
|
|
35849c57dc | ||
|
|
27504658ce | ||
|
|
db323348c7 | ||
|
|
edb1b5f333 | ||
|
|
2d8d377ddc | ||
|
|
057dd930b4 | ||
|
|
25b944e3e6 | ||
|
|
f1456f9707 | ||
|
|
c88e6827b7 | ||
|
|
bc3e3ae029 | ||
|
|
3191ac13e5 | ||
|
|
4f810809c8 | ||
|
|
d622949d26 | ||
|
|
d95b662542 | ||
|
|
d88d3122c8 | ||
|
|
a7f717c59c | ||
|
|
ba49981f17 | ||
|
|
05ae55b172 | ||
|
|
b5877ebe44 | ||
|
|
f9a03215b8 | ||
|
|
ff0980c4c1 | ||
|
|
1a58d3f08b | ||
|
|
b71027f622 | ||
|
|
84c03c6f26 | ||
|
|
bf455bc316 | ||
|
|
6af0378916 | ||
|
|
de154065fe | ||
|
|
459e9b7847 | ||
|
|
f127323c33 | ||
|
|
3fc245d829 | ||
|
|
542793a534 | ||
|
|
18c231de29 | ||
|
|
8e78857836 | ||
|
|
c7efbf590e | ||
|
|
5771085280 | ||
|
|
76d2419228 | ||
|
|
ffbf957fa3 | ||
|
|
ae3532e9ec | ||
|
|
5cd2ef903a |
@@ -13,16 +13,20 @@ max_line_length = 100
|
||||
indent_style = tab
|
||||
|
||||
[*.{md,rst}]
|
||||
max_line_length = unset
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{sh,ac}]
|
||||
indent_size = 2
|
||||
[*.sh]
|
||||
indent_size = 4
|
||||
|
||||
[build_tools/release.sh]
|
||||
max_line_length = 72
|
||||
|
||||
[Dockerfile]
|
||||
indent_size = 2
|
||||
|
||||
[share/{completions,functions}/**.fish]
|
||||
max_line_length = off
|
||||
max_line_length = unset
|
||||
|
||||
[{COMMIT_EDITMSG,git-revise-todo}]
|
||||
max_line_length = 80
|
||||
[{COMMIT_EDITMSG,git-revise-todo,*.jjdescription}]
|
||||
max_line_length = 72
|
||||
|
||||
14
.github/actions/install-sphinx-markdown-builder/action.yml
vendored
Normal file
14
.github/actions/install-sphinx-markdown-builder/action.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: Install sphinx-markdown-builder
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- shell: bash
|
||||
run: |
|
||||
set -x
|
||||
commit=b259de1dc97573a71470a1d71c3d83535934136b
|
||||
pip install git+https://github.com/krobelus/sphinx-markdown-builder@"$commit"
|
||||
python -c 'import sphinx_markdown_builder'
|
||||
20
.github/actions/rust-toolchain@oldest-supported/action.yml
vendored
Normal file
20
.github/actions/rust-toolchain@oldest-supported/action.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Oldest Supported Rust Toolchain
|
||||
|
||||
inputs:
|
||||
targets:
|
||||
description: Comma-separated list of target triples to install for this toolchain
|
||||
required: false
|
||||
components:
|
||||
description: Comma-separated list of components to be additionally installed
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@1.70
|
||||
with:
|
||||
targets: ${{ inputs.targets }}
|
||||
components: ${{ inputs.components}}
|
||||
20
.github/actions/rust-toolchain@stable/action.yml
vendored
Normal file
20
.github/actions/rust-toolchain@stable/action.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Stable Rust Toolchain
|
||||
|
||||
inputs:
|
||||
targets:
|
||||
description: Comma-separated list of target triples to install for this toolchain
|
||||
required: false
|
||||
components:
|
||||
description: Comma-separated list of components to be additionally installed
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@1.89
|
||||
with:
|
||||
targets: ${{ inputs.targets }}
|
||||
components: ${{ inputs.components }}
|
||||
42
.github/workflows/mac_codesign.yml
vendored
42
.github/workflows/mac_codesign.yml
vendored
@@ -1,42 +0,0 @@
|
||||
name: macOS build and codesign
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Enables manual trigger from GitHub UI
|
||||
|
||||
jobs:
|
||||
build-and-code-sign:
|
||||
runs-on: macos-latest
|
||||
environment: macos-codesign
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust 1.73.0
|
||||
uses: dtolnay/rust-toolchain@1.73.0
|
||||
with:
|
||||
targets: x86_64-apple-darwin
|
||||
- name: Install Rust Stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin
|
||||
- name: build-and-codesign
|
||||
run: |
|
||||
cargo install apple-codesign
|
||||
mkdir -p "$FISH_ARTEFACT_PATH"
|
||||
echo "$MAC_CODESIGN_APP_P12_BASE64" | base64 --decode > /tmp/app.p12
|
||||
echo "$MAC_CODESIGN_INSTALLER_P12_BASE64" | base64 --decode > /tmp/installer.p12
|
||||
echo "$MACOS_NOTARIZE_JSON" > /tmp/notarize.json
|
||||
./build_tools/make_pkg.sh -s -f /tmp/app.p12 -i /tmp/installer.p12 -p "$MAC_CODESIGN_PASSWORD" -n -j /tmp/notarize.json
|
||||
rm /tmp/installer.p12 /tmp/app.p12 /tmp/notarize.json
|
||||
env:
|
||||
MAC_CODESIGN_APP_P12_BASE64: ${{ secrets.MAC_CODESIGN_APP_P12_BASE64 }}
|
||||
MAC_CODESIGN_INSTALLER_P12_BASE64: ${{ secrets.MAC_CODESIGN_INSTALLER_P12_BASE64 }}
|
||||
MAC_CODESIGN_PASSWORD: ${{ secrets.MAC_CODESIGN_PASSWORD }}
|
||||
MACOS_NOTARIZE_JSON: ${{ secrets.MACOS_NOTARIZE_JSON }}
|
||||
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
|
||||
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
FISH_ARTEFACT_PATH: /tmp/fish-built
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macOS Artefacts
|
||||
path: /tmp/fish-built/*
|
||||
if-no-files-found: error
|
||||
1
.github/workflows/main.yml
vendored
1
.github/workflows/main.yml
vendored
@@ -22,6 +22,7 @@ jobs:
|
||||
sudo apt install gettext libpcre2-dev python3-pexpect tmux
|
||||
# Generate a locale that uses a comma as decimal separator.
|
||||
sudo locale-gen fr_FR.UTF-8
|
||||
- uses: ./.github/actions/install-sphinx-markdown-builder
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
|
||||
190
.github/workflows/release.yml
vendored
Normal file
190
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
name: Create a new release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to release (tag name)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
is-release-tag:
|
||||
name: Pre-release checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Check if the pushed tag looks like a release
|
||||
run: |
|
||||
set -x
|
||||
commit_subject=$(git log -1 --format=%s)
|
||||
tag=$(git describe)
|
||||
[ "$commit_subject" = "Release $tag" ]
|
||||
|
||||
|
||||
source-tarball:
|
||||
needs: [is-release-tag]
|
||||
name: Create the source tarball
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
tarball-name: ${{ steps.version.outputs.tarball-name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Install dependencies
|
||||
run: sudo apt install cmake gettext ninja-build python3-pip python3-sphinx
|
||||
- uses: ./.github/actions/install-sphinx-markdown-builder
|
||||
- name: Create tarball
|
||||
run: |
|
||||
set -x
|
||||
mkdir /tmp/fish-built
|
||||
FISH_ARTEFACT_PATH=/tmp/fish-built ./build_tools/make_tarball.sh
|
||||
relnotes=/tmp/fish-built/release-notes.md
|
||||
# Need history since the last release (i.e. tag) for stats.
|
||||
git fetch --tags
|
||||
git fetch --unshallow
|
||||
sh -x ./build_tools/release-notes.sh >"$relnotes"
|
||||
# Delete title
|
||||
sed -n 1p "$relnotes" | grep -q "^## fish .*"
|
||||
sed -n 2p "$relnotes" | grep -q '^$'
|
||||
sed -i 1,2d "$relnotes"
|
||||
- name: Upload tarball artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
path: |
|
||||
/tmp/fish-built/fish-${{ inputs.version }}.tar.xz
|
||||
/tmp/fish-built/release-notes.md
|
||||
if-no-files-found: error
|
||||
|
||||
packages-for-linux:
|
||||
needs: [is-release-tag]
|
||||
name: Build single-file fish for Linux (experimental)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Install Rust Stable
|
||||
uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
targets: x86_64-unknown-linux-musl,aarch64-unknown-linux-musl
|
||||
- name: Install dependencies
|
||||
run: sudo apt install crossbuild-essential-arm64 musl-tools python3-sphinx
|
||||
- name: Build statically-linked executables
|
||||
run: |
|
||||
set -x
|
||||
CFLAGS="-D_FORTIFY_SOURCE=2" \
|
||||
CMAKE_WITH_GETTEXT=0 \
|
||||
CC=aarch64-linux-gnu-gcc \
|
||||
RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-lgcc -C link-arg=-D_FORTIFY_SOURCE=0" \
|
||||
cargo build --release --target aarch64-unknown-linux-musl --bin fish
|
||||
cargo build --release --target x86_64-unknown-linux-musl --bin fish
|
||||
- name: Compress
|
||||
run: |
|
||||
set -x
|
||||
for arch in x86_64 aarch64; do
|
||||
tar -cazf fish-$(git describe)-linux-$arch.tar.xz \
|
||||
-C target/$arch-unknown-linux-musl/release fish
|
||||
done
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Static builds for Linux
|
||||
path: fish-${{ inputs.version }}-linux-*.tar.xz
|
||||
if-no-files-found: error
|
||||
|
||||
create-draft-release:
|
||||
needs:
|
||||
- is-release-tag
|
||||
- source-tarball
|
||||
- packages-for-linux
|
||||
name: Create release draft
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
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
|
||||
with:
|
||||
tag_name: ${{ inputs.version }}
|
||||
name: fish ${{ inputs.version }}
|
||||
body_path: /tmp/artifacts/release-notes.md
|
||||
draft: true
|
||||
files: |
|
||||
/tmp/artifacts/fish-${{ inputs.version }}.tar.xz
|
||||
/tmp/artifacts/fish-${{ inputs.version }}-linux-*.tar.xz
|
||||
|
||||
packages-for-macos:
|
||||
needs: [is-release-tag, create-draft-release]
|
||||
name: Build packages for macOS
|
||||
runs-on: macos-latest
|
||||
environment: macos-codesign
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Workaround for https://github.com/actions/checkout/issues/882
|
||||
ref: ${{ inputs.version }}
|
||||
- name: Install Rust
|
||||
uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||
with:
|
||||
targets: x86_64-apple-darwin
|
||||
- name: Install Rust Stable
|
||||
uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin
|
||||
- name: Build and codesign
|
||||
run: |
|
||||
die() { echo >&2 "$*"; exit 1; }
|
||||
[ -n "$MAC_CODESIGN_APP_P12_BASE64" ] || die "Missing MAC_CODESIGN_APP_P12_BASE64"
|
||||
[ -n "$MAC_CODESIGN_INSTALLER_P12_BASE64" ] || die "Missing MAC_CODESIGN_INSTALLER_P12_BASE64"
|
||||
[ -n "$MAC_CODESIGN_PASSWORD" ] || die "Missing MAC_CODESIGN_PASSWORD"
|
||||
[ -n "$MACOS_NOTARIZE_JSON" ] || die "Missing MACOS_NOTARIZE_JSON"
|
||||
set -x
|
||||
export FISH_ARTEFACT_PATH=/tmp/fish-built
|
||||
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
|
||||
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
|
||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||
cargo install apple-codesign
|
||||
mkdir -p "$FISH_ARTEFACT_PATH"
|
||||
echo "$MAC_CODESIGN_APP_P12_BASE64" | base64 --decode >/tmp/app.p12
|
||||
echo "$MAC_CODESIGN_INSTALLER_P12_BASE64" | base64 --decode >/tmp/installer.p12
|
||||
echo "$MACOS_NOTARIZE_JSON" >/tmp/notarize.json
|
||||
./build_tools/make_macos_pkg.sh -s -f /tmp/app.p12 \
|
||||
-i /tmp/installer.p12 -p "$MAC_CODESIGN_PASSWORD" \
|
||||
-n -j /tmp/notarize.json
|
||||
version=$(git describe)
|
||||
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.app.zip" ]
|
||||
[ -f "${FISH_ARTEFACT_PATH}/fish-$version.pkg" ]
|
||||
rm /tmp/installer.p12 /tmp/app.p12 /tmp/notarize.json
|
||||
env:
|
||||
MAC_CODESIGN_APP_P12_BASE64: ${{ secrets.MAC_CODESIGN_APP_P12_BASE64 }}
|
||||
MAC_CODESIGN_INSTALLER_P12_BASE64: ${{ secrets.MAC_CODESIGN_INSTALLER_P12_BASE64 }}
|
||||
MAC_CODESIGN_PASSWORD: ${{ secrets.MAC_CODESIGN_PASSWORD }}
|
||||
MACOS_NOTARIZE_JSON: ${{ secrets.MACOS_NOTARIZE_JSON }}
|
||||
- name: Add macOS packages to the release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
version=$(git describe)
|
||||
gh release upload $version \
|
||||
/tmp/fish-built/fish-$version.app.zip \
|
||||
/tmp/fish-built/fish-$version.pkg
|
||||
47
.github/workflows/staticbuild.yml
vendored
47
.github/workflows/staticbuild.yml
vendored
@@ -1,47 +0,0 @@
|
||||
name: staticbuilds
|
||||
|
||||
on:
|
||||
# release:
|
||||
# types: [published]
|
||||
# schedule:
|
||||
# - cron: "14 13 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CTEST_PARALLEL_LEVEL: "1"
|
||||
CMAKE_BUILD_PARALLEL_LEVEL: "4"
|
||||
|
||||
jobs:
|
||||
staticbuilds:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@1.70
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Prepare
|
||||
run: |
|
||||
sudo apt install python3-sphinx
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
sudo apt install musl-tools crossbuild-essential-arm64 -y
|
||||
- name: Build
|
||||
run: |
|
||||
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" CMAKE_WITH_GETTEXT=0 CC=aarch64-linux-gnu-gcc RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-lgcc -C link-arg=-D_FORTIFY_SOURCE=0" cargo build --release --target aarch64-unknown-linux-musl
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
- name: Compress
|
||||
run: |
|
||||
tar -cazf fish-amd64.tar.xz -C target/x86_64-unknown-linux-musl/release/ fish{,_indent,_key_reader}
|
||||
tar -cazf fish-aarch64.tar.xz -C target/aarch64-unknown-linux-musl/release/ fish{,_indent,_key_reader}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fish
|
||||
path: |
|
||||
fish-amd64.tar.xz
|
||||
fish-aarch64.tar.xz
|
||||
retention-days: 14
|
||||
@@ -1,3 +1,77 @@
|
||||
fish 4.0.9 (released September 27, 2025)
|
||||
========================================
|
||||
|
||||
This release fixes:
|
||||
|
||||
- a regression in 4.0.6 causing shifted keys to not be inserted on some terminals (:issue:`11813`).
|
||||
- a regression in 4.0.6 causing the build to fail on systems where ``char`` is unsigned (:issue:`11804`).
|
||||
- a regression in 4.0.0 causing a crash on an invalid :doc:`bg <cmds/bg>` invocation.
|
||||
|
||||
--------------
|
||||
|
||||
fish 4.0.8 (released September 18, 2025)
|
||||
========================================
|
||||
|
||||
This release fixes a regression in 4.0.6 that caused user bindings to be shadowed by either fish's or a plugin's bindings (:issue:`11803`).
|
||||
|
||||
--------------
|
||||
|
||||
fish 4.0.6 (released September 12, 2025)
|
||||
========================================
|
||||
|
||||
This release of fish fixes a number of issues identified in fish 4.0.2:
|
||||
|
||||
- fish now properly inherits $PATH under Windows WSL2 (:issue:`11354`).
|
||||
- Remote filesystems are detected properly again on non-Linux systems.
|
||||
- the :doc:`printf <cmds/printf>` builtin no longer miscalculates width of multi-byte characters (:issue:`11412`).
|
||||
- For many years, fish has been "relocatable" -- it was possible to move the entire ``CMAKE_INSTALL_PREFIX`` and fish would use paths relative to its binary.
|
||||
Only gettext locale paths were still determined purely at compile time, which has been fixed.
|
||||
- the :doc:`commandline <cmds/commandline>` builtin failed to print the commandline set by a ``commandline -C`` invocation, which broke some completion scripts.
|
||||
This has been corrected (:issue:`11423`).
|
||||
- To work around terminals that fail to parse Operating System Command (OSC) sequences, a temporary feature flag has been added.
|
||||
It allows you to disable prompt marking (OSC 133) by running (once) ``set -Ua fish_features no-mark-prompt`` and restarting fish (:issue:`11749`).
|
||||
- The routines to save history and universal variables have seen some robustness improvements.
|
||||
- builtin :doc:`status current-command <cmds/status>` no longer prints a trailing blank line.
|
||||
- A crash displaying multi-line quoted command substitutions has been fixed (:issue:`11444`).
|
||||
- Commands like ``set fish_complete_path ...`` accidentally disabled completion autoloading, which has been corrected.
|
||||
- ``nmcli`` completions have been fixed to query network information dynamically instead of only when completing the first time.
|
||||
- Git completions no longer print an error when no `git-foo` executable is in :envvar:`PATH`.
|
||||
- Custom completions like ``complete foo -l long -xa ...`` that use the output of ``commandline -t``.
|
||||
on a command-line like ``foo --long=`` have been invalidated by a change in 4.0; the completion scripts have been adjusted accordingly (:issue:`11508`).
|
||||
- Some completions were misinterpreted, which caused garbage to be displayed in the completion list. This has been fixed.
|
||||
- fish no longer interprets invalid control sequences from the terminal as if they were :kbd:`alt-[` or :kbd:`alt-o` key strokes.
|
||||
- :doc:`bind <cmds/bind>` has been taught about the :kbd:`printscreen` and :kbd:`menu` keys.
|
||||
- :kbd:`alt-delete` now deletes the word right of the cursor.
|
||||
- :kbd:`ctrl-alt-h` erases the last word again (:issue:`11548`).
|
||||
- :kbd:`alt-left` :kbd:`alt-right` were misinterpreted because they send unexpected sequences on some terminals; a workaround has been added. (:issue:`11479`).
|
||||
- Key bindings like ``bind shift-A`` are no longer accepted; use ``bind shift-a`` or ``bind A``.
|
||||
- Key bindings like ``bind shift-a`` take precedence over ``bind A`` when the key event included the shift modifier.
|
||||
- Bindings using shift with non-ASCII letters (such as :kbd:`ctrl-shift-ä`) are now supported.
|
||||
- Bindings with modifiers such as ``bind ctrl-w`` work again on non-Latin keyboard layouts such as a Russian one.
|
||||
This is implemented by allowing key events such as :kbd:`ctrl-ц` to match bindings of the corresponding Latin key, using the kitty keyboard protocol's base layout key (:issue:`11520`).
|
||||
- Vi mode: The cursor position after pasting via :kbd:`p` has been corrected.
|
||||
- Vi mode: Trying to replace the last character via :kbd:`r` no longer replaces the last-but-one character (:issue:`11484`),
|
||||
|
||||
fish 4.0.2 (released April 20, 2025)
|
||||
====================================
|
||||
|
||||
This release of fish fixes a number of issues identified in fish 4.0.1:
|
||||
|
||||
- Completions are quoted, rather than backslash-escaped, only if the completion is unambiguous. Continuing to edit the token is therefore easier (:issue:`11271`). This changes the behavior introduced in 4.0.0 where all completions were quoted.
|
||||
- The warning when the terminfo database can't be found has been downgraded to a log message. fish will act as if the terminal behaves like xterm-256color, which is correct for the vast majority of cases (:issue:`11277`, :issue:`11290`).
|
||||
- Key combinations using the super (Windows/command) key can now (actually) be bound using the :kbd:`super-` prefix (:issue:`11217`). This was listed in the release notes for 4.0.1 but did not work correctly.
|
||||
- :doc:`function <cmds/function>` is stricter about argument parsing, rather than allowing additional parameters to be silently ignored (:issue:`11295`).
|
||||
- Using parentheses in the :doc:`test <cmds/test>` builtin works correctly, following a regression in 4.0.0 where they were not recognized (:issue:`11387`).
|
||||
- :kbd:`delete` in Vi mode when Num Lock is active will work correctly (:issue:`11303`).
|
||||
- Abbreviations cannot alter the command-line contents, preventing a crash (:issue:`11324`).
|
||||
- Improvements to various completions, including new completions for ``wl-randr`` (:issue:`11301`), performance improvements for ``cargo`` completions by avoiding network requests (:issue:`11347`), and other improvements for ``btrfs`` (:issue:`11320`), ``cryptsetup`` (:issue:`11315`), ``git`` (:issue:`11319`, :issue:`11322`, :issue:`11323`), ``jj`` (:issue:`11046`), and ``systemd-analyze`` (:issue:`11314`).
|
||||
- The Mercurial (``hg``) prompt can handle working directories that contain an embedded newline, rather than producing errors (:issue:`11348`).
|
||||
- A number of crashes have been fixed. Triggers include prompts containing backspace characters (:issue:`11280`), history pager search (:issue:`11355`), invalid UTF-8 in :doc:`read <cmds/read>` (:issue:`11383`), and the ``kill-selection`` binding (:issue:`11367`).
|
||||
- A race condition in the test suite has been fixed (:issue:`11254`), and a test for fish versioning relaxed to support downstream distributors' modifications (:issue:`11173`).
|
||||
- Small improvements to the documentation (:issue:`11264`, :issue:`11329`, :issue:`11361`).
|
||||
|
||||
--------------
|
||||
|
||||
fish 4.0.1 (released March 12, 2025)
|
||||
====================================
|
||||
|
||||
|
||||
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -112,7 +112,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "4.0.1"
|
||||
version = "4.0.9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
@@ -139,6 +139,8 @@ name = "fish-printf"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"widestring",
|
||||
]
|
||||
|
||||
@@ -567,6 +569,18 @@ version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
|
||||
@@ -16,7 +16,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.0.1"
|
||||
version = "4.0.9"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
default-run = "fish"
|
||||
|
||||
16
build.rs
16
build.rs
@@ -227,7 +227,7 @@ fn has_small_stack(_: &Target) -> Result<bool, Box<dyn Error>> {
|
||||
}
|
||||
|
||||
fn setup_paths() {
|
||||
fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
fn get_path(name: &str, default: &str, onvar: &Path) -> PathBuf {
|
||||
let mut var = PathBuf::from(env::var(name).unwrap_or(default.to_string()));
|
||||
if var.is_relative() {
|
||||
var = onvar.join(var);
|
||||
@@ -250,7 +250,7 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
rsconf::rebuild_if_env_changed("PREFIX");
|
||||
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
|
||||
|
||||
let datadir = get_path("DATADIR", "share/", prefix.clone());
|
||||
let datadir = get_path("DATADIR", "share/", &prefix);
|
||||
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("DATADIR");
|
||||
|
||||
@@ -261,7 +261,7 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
};
|
||||
rsconf::set_env_value("DATADIR_SUBDIR", datadir_subdir);
|
||||
|
||||
let bindir = get_path("BINDIR", "bin/", prefix.clone());
|
||||
let bindir = get_path("BINDIR", "bin/", &prefix);
|
||||
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("BINDIR");
|
||||
|
||||
@@ -270,16 +270,16 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
// If we get our prefix from $HOME, we should use the system's /etc/
|
||||
// ~/.local/share/etc/ makes no sense
|
||||
if prefix_from_home { "/etc/" } else { "etc/" },
|
||||
datadir.clone(),
|
||||
&datadir,
|
||||
);
|
||||
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("SYSCONFDIR");
|
||||
|
||||
let localedir = get_path("LOCALEDIR", "locale/", datadir.clone());
|
||||
let localedir = get_path("LOCALEDIR", "locale/", &datadir);
|
||||
rsconf::set_env_value("LOCALEDIR", localedir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("LOCALEDIR");
|
||||
|
||||
let docdir = get_path("DOCDIR", "doc/fish", datadir.clone());
|
||||
let docdir = get_path("DOCDIR", "doc/fish", &datadir);
|
||||
rsconf::set_env_value("DOCDIR", docdir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("DOCDIR");
|
||||
}
|
||||
@@ -292,7 +292,7 @@ fn get_version(src_dir: &Path) -> String {
|
||||
return var;
|
||||
}
|
||||
|
||||
let path = PathBuf::from(src_dir).join("version");
|
||||
let path = src_dir.join("version");
|
||||
if let Ok(strver) = read_to_string(path) {
|
||||
return strver.to_string();
|
||||
}
|
||||
@@ -321,7 +321,7 @@ fn get_version(src_dir: &Path) -> String {
|
||||
// or because it refused (safe.directory applies to `git describe`!)
|
||||
// So we read the SHA ourselves.
|
||||
fn get_git_hash() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let gitdir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".git");
|
||||
let gitdir = Path::new(env!("CARGO_MANIFEST_DIR")).join(".git");
|
||||
|
||||
// .git/HEAD contains ref: refs/heads/branch
|
||||
let headpath = gitdir.join("HEAD");
|
||||
|
||||
177
build_tools/make_macos_pkg.sh
Executable file
177
build_tools/make_macos_pkg.sh
Executable file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Script to produce an OS X installer .pkg and .app(.zip)
|
||||
|
||||
usage() {
|
||||
echo "Build macOS packages, optionally signing and notarizing them."
|
||||
echo "Usage: $0 options"
|
||||
echo "Options:"
|
||||
echo " -s Enables code signing"
|
||||
echo " -f <APP_KEY.p12> Path to .p12 file for application signing"
|
||||
echo " -i <INSTALLER_KEY.p12> Path to .p12 file for installer signing"
|
||||
echo " -p <PASSWORD> Password for the .p12 files (necessary to access the certificates)"
|
||||
echo " -e <entitlements file> (Optional) Path to an entitlements XML file"
|
||||
echo " -n Enables notarization. This will fail if code signing is not also enabled."
|
||||
echo " -j <API_KEY.JSON> Path to JSON file generated with \`rcodesign encode-app-store-connect-api-key\` (required for notarization)"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
SIGN=
|
||||
NOTARIZE=
|
||||
|
||||
ARM64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=11.0'
|
||||
X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.9'
|
||||
|
||||
# As of this writing, the most recent Rust release supports macOS back to 10.12.
|
||||
# The first supported version of macOS on arm64 is 10.15, so any Rust is fine for arm64.
|
||||
# We wish to support back to 10.9 on x86-64; the last version of Rust to support that is
|
||||
# version 1.73.0.
|
||||
RUST_VERSION_X86_64=1.70.0
|
||||
|
||||
while getopts "sf:i:p:e:nj:" opt; do
|
||||
case $opt in
|
||||
s) SIGN=1;;
|
||||
f) P12_APP_FILE=$(realpath "$OPTARG");;
|
||||
i) P12_INSTALL_FILE=$(realpath "$OPTARG");;
|
||||
p) P12_PASSWORD="$OPTARG";;
|
||||
e) ENTITLEMENTS_FILE=$(realpath "$OPTARG");;
|
||||
n) NOTARIZE=1;;
|
||||
j) API_KEY_FILE=$(realpath "$OPTARG");;
|
||||
\?) usage;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$SIGN" ] && { [ -z "$P12_APP_FILE" ] || [ -z "$P12_INSTALL_FILE" ] || [ -z "$P12_PASSWORD" ]; }; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
VERSION=$(build_tools/git_version_gen.sh --stdout 2>/dev/null)
|
||||
|
||||
echo "Version is $VERSION"
|
||||
|
||||
PKGDIR=$(mktemp -d)
|
||||
echo "$PKGDIR"
|
||||
|
||||
SRC_DIR=$PWD
|
||||
OUTPUT_PATH=${FISH_ARTEFACT_PATH:-~/fish_built}
|
||||
|
||||
mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/intermediates" "$PKGDIR/dst"
|
||||
|
||||
# Build and install for arm64.
|
||||
# Pass FISH_USE_SYSTEM_PCRE2=OFF because a system PCRE2 on macOS will not be signed by fish,
|
||||
# and will probably not be built universal, so the package will fail to validate/run on other systems.
|
||||
# Note CMAKE_OSX_ARCHITECTURES is still relevant for the Mac app.
|
||||
{ cd "$PKGDIR/build_arm64" \
|
||||
&& cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
|
||||
-DWITH_GETTEXT=OFF \
|
||||
-DRust_CARGO_TARGET=aarch64-apple-darwin \
|
||||
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
|
||||
-DFISH_USE_SYSTEM_PCRE2=OFF \
|
||||
"$SRC_DIR" \
|
||||
&& env $ARM64_DEPLOY_TARGET make VERBOSE=1 -j 12 \
|
||||
&& env DESTDIR="$PKGDIR/root/" $ARM64_DEPLOY_TARGET make install;
|
||||
}
|
||||
|
||||
# Build for x86-64 but do not install; instead we will make some fat binaries inside the root.
|
||||
# Set RUST_VERSION_X86_64 to the last version of Rust that supports macOS 10.9.
|
||||
{ cd "$PKGDIR/build_x86_64" \
|
||||
&& cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
|
||||
-DWITH_GETTEXT=OFF \
|
||||
-DRust_TOOLCHAIN="$RUST_VERSION_X86_64" \
|
||||
-DRust_CARGO_TARGET=x86_64-apple-darwin \
|
||||
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
|
||||
-DFISH_USE_SYSTEM_PCRE2=OFF "$SRC_DIR" \
|
||||
&& env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; }
|
||||
|
||||
# Fatten them up.
|
||||
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
chmod 755 "$FILE"
|
||||
done
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing executables"
|
||||
ARGS=(
|
||||
--p12-file "$P12_APP_FILE"
|
||||
--p12-password "$P12_PASSWORD"
|
||||
--code-signature-flags runtime
|
||||
--for-notarization
|
||||
)
|
||||
if [ -n "$ENTITLEMENTS_FILE" ]; then
|
||||
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
|
||||
fi
|
||||
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "$FILE")
|
||||
done
|
||||
fi
|
||||
|
||||
pkgbuild --scripts "$SRC_DIR/build_tools/osx_package_scripts" --root "$PKGDIR/root/" --identifier 'com.ridiculousfish.fish-shell-pkg' --version "$VERSION" "$PKGDIR/intermediates/fish.pkg"
|
||||
productbuild --package-path "$PKGDIR/intermediates" --distribution "$SRC_DIR/build_tools/osx_distribution.xml" --resources "$SRC_DIR/build_tools/osx_package_resources/" "$OUTPUT_PATH/fish-$VERSION.pkg"
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing installer"
|
||||
ARGS=(
|
||||
--p12-file "$P12_INSTALL_FILE"
|
||||
--p12-password "$P12_PASSWORD"
|
||||
--code-signature-flags runtime
|
||||
--for-notarization
|
||||
)
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "$OUTPUT_PATH/fish-$VERSION.pkg")
|
||||
fi
|
||||
|
||||
# Make the app
|
||||
(cd "$PKGDIR/build_arm64" && env $ARM64_DEPLOY_TARGET make -j 12 fish_macapp)
|
||||
(cd "$PKGDIR/build_x86_64" && env $X86_64_DEPLOY_TARGET make -j 12 fish_macapp)
|
||||
|
||||
# Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
|
||||
cd "$PKGDIR/build_arm64"
|
||||
for FILE in fish.app/Contents/Resources/base/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
|
||||
# macho-universal-create screws up the permissions.
|
||||
chmod 755 "$FILE"
|
||||
done
|
||||
|
||||
if test -n "$SIGN"; then
|
||||
echo "Signing app"
|
||||
ARGS=(
|
||||
--p12-file "$P12_APP_FILE"
|
||||
--p12-password "$P12_PASSWORD"
|
||||
--code-signature-flags runtime
|
||||
--for-notarization
|
||||
)
|
||||
if [ -n "$ENTITLEMENTS_FILE" ]; then
|
||||
ARGS+=(--entitlements-xml-file "$ENTITLEMENTS_FILE")
|
||||
fi
|
||||
(set +x; rcodesign sign "${ARGS[@]}" "fish.app")
|
||||
|
||||
fi
|
||||
|
||||
cp -R "fish.app" "$OUTPUT_PATH/fish-$VERSION.app"
|
||||
cd "$OUTPUT_PATH"
|
||||
|
||||
# Maybe notarize.
|
||||
if test -n "$NOTARIZE"; then
|
||||
echo "Notarizing"
|
||||
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.pkg"
|
||||
rcodesign notarize --staple --wait --max-wait-seconds 1800 --api-key-file "$API_KEY_FILE" "$OUTPUT_PATH/fish-$VERSION.app"
|
||||
fi
|
||||
|
||||
# Zip it up.
|
||||
zip -r "fish-$VERSION.app.zip" "fish-$VERSION.app" && rm -Rf "fish-$VERSION.app"
|
||||
|
||||
rm -rf "$PKGDIR"
|
||||
@@ -3,18 +3,18 @@
|
||||
# Script to produce an OS X installer .pkg and .app(.zip)
|
||||
|
||||
usage() {
|
||||
echo "Build macOS packages, optionally signing and notarizing them."
|
||||
echo "Usage: $0 options"
|
||||
echo "Options:"
|
||||
echo " -s Enables code signing"
|
||||
echo " -f <APP_KEY.p12> Path to .p12 file for application signing"
|
||||
echo " -i <INSTALLER_KEY.p12> Path to .p12 file for installer signing"
|
||||
echo " -p <PASSWORD> Password for the .p12 files (necessary to access the certificates)"
|
||||
echo " -e <entitlements file> (Optional) Path to an entitlements XML file"
|
||||
echo " -n Enables notarization. This will fail if code signing is not also enabled."
|
||||
echo " -j <API_KEY.JSON> Path to JSON file generated with `rcodesign encode-app-store-connect-api-key` (required for notarization)"
|
||||
echo
|
||||
exit 1
|
||||
echo "Build macOS packages, optionally signing and notarizing them."
|
||||
echo "Usage: $0 options"
|
||||
echo "Options:"
|
||||
echo " -s Enables code signing"
|
||||
echo " -f <APP_KEY.p12> Path to .p12 file for application signing"
|
||||
echo " -i <INSTALLER_KEY.p12> Path to .p12 file for installer signing"
|
||||
echo " -p <PASSWORD> Password for the .p12 files (necessary to access the certificates)"
|
||||
echo " -e <entitlements file> (Optional) Path to an entitlements XML file"
|
||||
echo " -n Enables notarization. This will fail if code signing is not also enabled."
|
||||
echo " -j <API_KEY.JSON> Path to JSON file generated with \`rcodesign encode-app-store-connect-api-key\` (required for notarization)"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
set -x
|
||||
@@ -30,35 +30,35 @@ X86_64_DEPLOY_TARGET='MACOSX_DEPLOYMENT_TARGET=10.9'
|
||||
# The first supported version of macOS on arm64 is 10.15, so any Rust is fine for arm64.
|
||||
# We wish to support back to 10.9 on x86-64; the last version of Rust to support that is
|
||||
# version 1.73.0.
|
||||
RUST_VERSION_X86_64=1.73.0
|
||||
RUST_VERSION_X86_64=1.70.0
|
||||
|
||||
while getopts "sf:i:p:e:nj:" opt; do
|
||||
case $opt in
|
||||
s) SIGN=1;;
|
||||
f) P12_APP_FILE=$(realpath "$OPTARG");;
|
||||
i) P12_INSTALL_FILE=$(realpath "$OPTARG");;
|
||||
p) P12_PASSWORD="$OPTARG";;
|
||||
e) ENTITLEMENTS_FILE=$(realpath "$OPTARG");;
|
||||
n) NOTARIZE=1;;
|
||||
j) API_KEY_FILE=$(realpath "$OPTARG");;
|
||||
\?) usage;;
|
||||
esac
|
||||
case $opt in
|
||||
s) SIGN=1;;
|
||||
f) P12_APP_FILE=$(realpath "$OPTARG");;
|
||||
i) P12_INSTALL_FILE=$(realpath "$OPTARG");;
|
||||
p) P12_PASSWORD="$OPTARG";;
|
||||
e) ENTITLEMENTS_FILE=$(realpath "$OPTARG");;
|
||||
n) NOTARIZE=1;;
|
||||
j) API_KEY_FILE=$(realpath "$OPTARG");;
|
||||
\?) usage;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -n "$SIGN" ] && ([ -z "$P12_APP_FILE" ] || [-z "$P12_INSTALL_FILE"] || [ -z "$P12_PASSWORD" ]); then
|
||||
usage
|
||||
if [ -n "$SIGN" ] && { [ -z "$P12_APP_FILE" ] || [ -z "$P12_INSTALL_FILE" ] || [ -z "$P12_PASSWORD" ]; }; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ -n "$NOTARIZE" ] && [ -z "$API_KEY_FILE" ]; then
|
||||
usage
|
||||
usage
|
||||
fi
|
||||
|
||||
VERSION=$(git describe --always --dirty 2>/dev/null)
|
||||
if test -z "$VERSION" ; then
|
||||
echo "Could not get version from git"
|
||||
if test -f version; then
|
||||
VERSION=$(cat version)
|
||||
fi
|
||||
echo "Could not get version from git"
|
||||
if test -f version; then
|
||||
VERSION=$(cat version)
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Version is $VERSION"
|
||||
@@ -76,7 +76,7 @@ mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/in
|
||||
# and will probably not be built universal, so the package will fail to validate/run on other systems.
|
||||
# Note CMAKE_OSX_ARCHITECTURES is still relevant for the Mac app.
|
||||
{ cd "$PKGDIR/build_arm64" \
|
||||
&& cmake \
|
||||
&& cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
|
||||
-DWITH_GETTEXT=OFF \
|
||||
@@ -91,7 +91,7 @@ mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/in
|
||||
# Build for x86-64 but do not install; instead we will make some fat binaries inside the root.
|
||||
# Set RUST_VERSION_X86_64 to the last version of Rust that supports macOS 10.9.
|
||||
{ cd "$PKGDIR/build_x86_64" \
|
||||
&& cmake \
|
||||
&& cmake \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-Wl,-ld_classic" \
|
||||
-DWITH_GETTEXT=OFF \
|
||||
@@ -99,11 +99,11 @@ mkdir -p "$PKGDIR/build_x86_64" "$PKGDIR/build_arm64" "$PKGDIR/root" "$PKGDIR/in
|
||||
-DRust_CARGO_TARGET=x86_64-apple-darwin \
|
||||
-DCMAKE_OSX_ARCHITECTURES='arm64;x86_64' \
|
||||
-DFISH_USE_SYSTEM_PCRE2=OFF "$SRC_DIR" \
|
||||
&& env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; }
|
||||
&& env $X86_64_DEPLOY_TARGET make VERBOSE=1 -j 12; }
|
||||
|
||||
# Fatten them up.
|
||||
for FILE in "$PKGDIR"/root/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/$(basename $FILE)"
|
||||
X86_FILE="$PKGDIR/build_x86_64/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
chmod 755 "$FILE"
|
||||
done
|
||||
@@ -145,7 +145,7 @@ fi
|
||||
# Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
|
||||
cd "$PKGDIR/build_arm64"
|
||||
for FILE in fish.app/Contents/Resources/base/usr/local/bin/*; do
|
||||
X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename $FILE)"
|
||||
X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename "$FILE")"
|
||||
rcodesign macho-universal-create --output "$FILE" "$FILE" "$X86_FILE"
|
||||
|
||||
# macho-universal-create screws up the permissions.
|
||||
|
||||
104
build_tools/release-notes.sh
Executable file
104
build_tools/release-notes.sh
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
workspace_root=$(dirname "$0")/..
|
||||
|
||||
relnotes_tmp=$(mktemp -d)
|
||||
mkdir -p "$relnotes_tmp/fake-workspace" "$relnotes_tmp/out"
|
||||
(
|
||||
cd "$workspace_root"
|
||||
cp -r doc_src CONTRIBUTING.rst README.rst "$relnotes_tmp/fake-workspace"
|
||||
)
|
||||
version=$(sed 's,^fish \(\S*\) .*,\1,; 1q' "$workspace_root/CHANGELOG.rst")
|
||||
previous_version=$(
|
||||
cd "$workspace_root"
|
||||
awk <CHANGELOG.rst '
|
||||
( /^fish \S*\.\S*\.\S* \(released .*\)$/ &&
|
||||
NR > 1 &&
|
||||
# Skip tags that have not been created yet..
|
||||
system("git rev-parse --verify >/dev/null --quiet refs/tags/"$2) == 0 \
|
||||
) {
|
||||
print $2; ok = 1; exit
|
||||
}
|
||||
END { exit !ok }
|
||||
'
|
||||
)
|
||||
minor_version=${version%.*}
|
||||
previous_minor_version=${previous_version%.*}
|
||||
{
|
||||
sed -n 1,2p <"$workspace_root/CHANGELOG.rst"
|
||||
|
||||
ListCommitters() {
|
||||
comm "$@" "$relnotes_tmp/committers-then" "$relnotes_tmp/committers-now"
|
||||
}
|
||||
(
|
||||
cd "$workspace_root"
|
||||
git log "$previous_version" --format="%aN" | sort -u >"$relnotes_tmp/committers-then"
|
||||
git log "$previous_version".. --format="%aN" | sort -u >"$relnotes_tmp/committers-now"
|
||||
ListCommitters -13 >"$relnotes_tmp/committers-new"
|
||||
ListCommitters -12 >"$relnotes_tmp/committers-returning"
|
||||
)
|
||||
if [ "$minor_version" != "$previous_minor_version" ]; then
|
||||
(
|
||||
cd "$workspace_root"
|
||||
num_commits=$(git log --no-merges --format=%H "$previous_version".. | wc -l)
|
||||
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," \
|
||||
" contributed by $num_authors authors, $num_new_authors of which are new committers."
|
||||
echo
|
||||
echo
|
||||
)
|
||||
fi
|
||||
|
||||
printf %s "$(awk <"$workspace_root/CHANGELOG.rst" '
|
||||
NR <= 2 || /^\.\. ignore / { next }
|
||||
/^===/ { exit }
|
||||
{ print }
|
||||
' | sed '$d')" |
|
||||
sed -e '$s/^----*$//' # Remove spurious transitions at the end of the document.
|
||||
|
||||
if [ "$minor_version" != "$previous_minor_version" ]; then
|
||||
JoinEscaped() {
|
||||
sed 's/\S/\\&/g' |
|
||||
awk '
|
||||
NR != 1 { printf ",\n" }
|
||||
{ printf "%s", $0 }
|
||||
END { printf "\n" }
|
||||
'
|
||||
}
|
||||
echo ""
|
||||
echo "---"
|
||||
echo ""
|
||||
echo "Thanks to everyone who contributed through issue discussions, code reviews, or code changes."
|
||||
echo
|
||||
printf "Welcome our new committers: "
|
||||
JoinEscaped <"$relnotes_tmp/committers-new"
|
||||
echo
|
||||
printf "Welcome back our returning committers: "
|
||||
JoinEscaped <"$relnotes_tmp/committers-returning"
|
||||
fi
|
||||
echo
|
||||
echo "---"
|
||||
echo
|
||||
echo "*Download links: To download the source code for fish, we suggest the file named \"fish-$version.tar.xz\". The file downloaded from \"Source code (tar.gz)\" will not build correctly.*"
|
||||
echo
|
||||
echo "*The files called fish-$version-linux-\*.tar.xz are experimental packages containing a single standalone ``fish`` binary for any Linux with the given CPU architecture.*"
|
||||
} >"$relnotes_tmp/fake-workspace"/CHANGELOG.rst
|
||||
|
||||
sphinx-build >&2 -j auto \
|
||||
-W -E -b markdown -c "$workspace_root/doc_src" \
|
||||
-d "$relnotes_tmp/doctree" "$relnotes_tmp/fake-workspace/doc_src" "$relnotes_tmp/out" \
|
||||
-D markdown_http_base="https://fishshell.com/docs/$minor_version" \
|
||||
-D markdown_uri_doc_suffix=".html" \
|
||||
-D markdown_github_flavored=1 \
|
||||
"$@"
|
||||
|
||||
# Skip changelog header
|
||||
sed -n 1p "$relnotes_tmp/out/relnotes.md" | grep -Fxq "# Release notes"
|
||||
sed -n 2p "$relnotes_tmp/out/relnotes.md" | grep -Fxq ''
|
||||
sed 1,2d "$relnotes_tmp/out/relnotes.md"
|
||||
|
||||
rm -r "$relnotes_tmp"
|
||||
229
build_tools/release.sh
Executable file
229
build_tools/release.sh
Executable file
@@ -0,0 +1,229 @@
|
||||
#!/bin/sh
|
||||
|
||||
{
|
||||
|
||||
set -ex
|
||||
|
||||
version=$1
|
||||
repository_owner=fish-shell
|
||||
remote=origin
|
||||
if [ -n "$2" ]; then
|
||||
set -u
|
||||
repository_owner=$2
|
||||
remote=$3
|
||||
set +u
|
||||
[ $# -eq 3 ]
|
||||
fi
|
||||
|
||||
[ -n "$version" ]
|
||||
|
||||
for tool in \
|
||||
bundle \
|
||||
gh \
|
||||
jq \
|
||||
ruby \
|
||||
timeout \
|
||||
; do
|
||||
if ! command -v "$tool" >/dev/null; then
|
||||
echo >&2 "$0: missing command: $1"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
repo_root="$(dirname "$0")/.."
|
||||
fish_site=$repo_root/../fish-site
|
||||
|
||||
for path in . "$fish_site"
|
||||
do
|
||||
if ! git -C "$path" diff HEAD --quiet ||
|
||||
git ls-files --others --exclude-standard | grep .; then
|
||||
echo >&2 "$0: index and worktree must be clean"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if git tag | grep -qxF "$version"; then
|
||||
echo >&2 "$0: tag $version already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
integration_branch=$(
|
||||
git for-each-ref --points-at=HEAD 'refs/heads/Integration_*' \
|
||||
--format='%(refname:strip=2)'
|
||||
)
|
||||
[ -n "$integration_branch" ] ||
|
||||
git merge-base --is-ancestor $remote/master HEAD
|
||||
|
||||
sed -n 1p CHANGELOG.rst | grep -q '^fish .*(released .*)$'
|
||||
sed -n 2p CHANGELOG.rst | grep -q '^===*$'
|
||||
|
||||
changelog_title="fish $version (released $(date +'%B %d, %Y'))"
|
||||
sed -i \
|
||||
-e "1c$changelog_title" \
|
||||
-e "2c$(printf %s "$changelog_title" | sed s/./=/g)" \
|
||||
CHANGELOG.rst
|
||||
|
||||
CommitVersion() {
|
||||
sed -i "s/^version = \".*\"/version = \"$1\"/g" Cargo.toml
|
||||
cargo fetch --offline
|
||||
git add CHANGELOG.rst Cargo.toml Cargo.lock
|
||||
git commit -m "$2
|
||||
|
||||
Created by ./build_tools/release.sh $version"
|
||||
}
|
||||
|
||||
CommitVersion "$version" "Release $version"
|
||||
|
||||
# N.B. this is not GPG-signed.
|
||||
git tag --annotate --message="Release $version" $version
|
||||
|
||||
git push $remote $version
|
||||
|
||||
TIMEOUT=
|
||||
gh() {
|
||||
command ${TIMEOUT:+timeout $TIMEOUT} \
|
||||
gh --repo "$repository_owner/fish-shell" "$@"
|
||||
}
|
||||
|
||||
gh workflow run release.yml --ref="$version" \
|
||||
--raw-field="version=$version"
|
||||
|
||||
run_id=
|
||||
while [ -z "$run_id" ] && sleep 5
|
||||
do
|
||||
run_id=$(gh run list \
|
||||
--json=databaseId --jq=.[].databaseId \
|
||||
--workflow=release.yml --limit=1 \
|
||||
--commit="$(git rev-parse "$version^{commit}")")
|
||||
done
|
||||
|
||||
# Update fishshell.com
|
||||
tag_oid=$(git rev-parse "$version")
|
||||
tmpdir=$(mktemp -d)
|
||||
# TODO This works on draft releases only if "gh" is configured to
|
||||
# have write access to the fish-shell repository. Unless we are fine
|
||||
# publishing the release at this point, we should at least fail if
|
||||
# "gh" doesn't have write access.
|
||||
while ! \
|
||||
gh release download "$version" --dir="$tmpdir" \
|
||||
--pattern="fish-$version.tar.xz"
|
||||
do
|
||||
TIMEOUT=30 gh run watch "$run_id" ||:
|
||||
sleep 5
|
||||
done
|
||||
actual_tag_oid=$(git ls-remote "$remote" |
|
||||
awk '$2 == "refs/tags/'"$version"'" { print $1 }')
|
||||
[ "$tag_oid" = "$actual_tag_oid" ]
|
||||
( cd "$tmpdir" && tar xf fish-$version.tar.xz )
|
||||
CopyDocs() {
|
||||
rm -rf "$fish_site/site/docs/$1"
|
||||
cp -r "$tmpdir/fish-$version/user_doc/html" "$fish_site/site/docs/$1"
|
||||
git -C $fish_site add "site/docs/$1"
|
||||
}
|
||||
minor_version=${version%.*}
|
||||
CopyDocs "$minor_version"
|
||||
latest_release=$(
|
||||
releases=$(git tag | grep '^[0-9]*\.[0-9]*\.[0-9]*.*' |
|
||||
sed $(: "De-prioritize release candidates (1.2.3-rc0)") \
|
||||
's/-/~/g' | LC_ALL=C sort --version-sort)
|
||||
printf %s\\n "$releases" | tail -1
|
||||
)
|
||||
if [ "$version" = "$latest_release" ]; then
|
||||
CopyDocs current
|
||||
fi
|
||||
rm -rf "$tmpdir"
|
||||
(
|
||||
cd "$fish_site"
|
||||
make
|
||||
git add -u
|
||||
! git ls-files --others --exclude-standard | grep .
|
||||
git commit --message="$(printf %s "\
|
||||
| Release $version (docs)
|
||||
|
|
||||
| Created by ../fish-shell/build_tools/release.sh
|
||||
" | sed 's,^\s*| \?,,')"
|
||||
)
|
||||
|
||||
# Approve macos-codesign
|
||||
# TODO what if current user can't approve?
|
||||
gh_pending_deployments() {
|
||||
command gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$repository_owner/fish-shell/actions/runs/$run_id/pending_deployments" \
|
||||
"$@"
|
||||
}
|
||||
while {
|
||||
environment_id=$(gh_pending_deployments | jq .[].environment.id)
|
||||
[ -z "$environment_id" ]
|
||||
}
|
||||
do
|
||||
sleep 5
|
||||
done
|
||||
echo '
|
||||
{
|
||||
"environment_ids": ['"$environment_id"'],
|
||||
"state": "approved",
|
||||
"comment": "Approved via ./build_tools/release.sh"
|
||||
}
|
||||
' |
|
||||
gh_pending_deployments -XPOST --input=-
|
||||
|
||||
# Await completion.
|
||||
gh run watch "$run_id"
|
||||
|
||||
while {
|
||||
! draft=$(gh release view "$version" --json=isDraft --jq=.isDraft) \
|
||||
|| [ "$draft" = true ]
|
||||
}
|
||||
do
|
||||
sleep 20
|
||||
done
|
||||
|
||||
(
|
||||
cd "$fish_site"
|
||||
make new-release
|
||||
git add -u
|
||||
! git ls-files --others --exclude-standard | grep .
|
||||
git commit --message="$(printf %s "\
|
||||
| Release $version (release list update)
|
||||
|
|
||||
| Created by ../fish-shell/build_tools/release.sh
|
||||
" | sed 's,^\s*| \?,,')"
|
||||
# This takes care to support remote names that are different from
|
||||
# fish-shell remote name. Also, support detached HEAD state.
|
||||
git push git@github.com:$repository_owner/fish-site HEAD:master
|
||||
)
|
||||
|
||||
if [ -n "$integration_branch" ]; then
|
||||
git push $remote "$version^{commit}":refs/heads/$integration_branch
|
||||
else
|
||||
changelog=$(cat - CHANGELOG.rst <<EOF
|
||||
fish ?.?.? (released ???)
|
||||
=========================
|
||||
|
||||
EOF
|
||||
)
|
||||
printf %s\\n "$changelog" >CHANGELOG.rst
|
||||
CommitVersion ${version}-snapshot "start new cycle"
|
||||
git push $remote HEAD:master
|
||||
fi
|
||||
|
||||
# TODO This can currently require a TTY for editing and password
|
||||
# prompts.
|
||||
if [ "$repository_owner" = fish-shell ]; then {
|
||||
mail=$(mktemp)
|
||||
cat >$mail <<EOF
|
||||
From: $(git var GIT_AUTHOR_IDENT | sed 's/ [0-9]* +[0-9]*$//')
|
||||
Subject: fish $version released
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/$version
|
||||
EOF
|
||||
git send-email --suppress-cc=all --confirm=always $mail \
|
||||
--to="fish-users Mailing List <fish-users@lists.sourceforge.net>"
|
||||
rm $mail
|
||||
} fi
|
||||
|
||||
exit
|
||||
|
||||
}
|
||||
@@ -24,7 +24,7 @@ add_executable(fish_macapp EXCLUDE_FROM_ALL
|
||||
|
||||
# Compute the version. Note this is done at generation time, not build time,
|
||||
# so cmake must be re-run after version changes for the app to be updated. But
|
||||
# generally this will be run by make_pkg.sh which always re-runs cmake.
|
||||
# generally this will be run by make_macos_pkg.sh which always re-runs cmake.
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/git_version_gen.sh --stdout
|
||||
COMMAND cut -d- -f1
|
||||
@@ -32,7 +32,7 @@ execute_process(
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
|
||||
# Note CMake appends .app, so the real output name will be fish.app.
|
||||
# Note CMake appends .app, so the real output name will be fish.app.
|
||||
# This target does not include the 'base' resource.
|
||||
set_target_properties(fish_macapp PROPERTIES OUTPUT_NAME "fish")
|
||||
|
||||
|
||||
1
debian/control
vendored
1
debian/control
vendored
@@ -20,7 +20,6 @@ Vcs-Browser: https://github.com/fish-shell/fish-shell
|
||||
|
||||
Package: fish
|
||||
Architecture: any
|
||||
Depends: bsdextrautils,
|
||||
Depends: bsdextrautils | bsdmainutils,
|
||||
file,
|
||||
gettext-base,
|
||||
|
||||
5
debian/rules
vendored
5
debian/rules
vendored
@@ -17,6 +17,9 @@ override_dh_auto_configure:
|
||||
dh_auto_configure --buildsystem=cmake -- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
|
||||
override_dh_clean:
|
||||
dh_clean
|
||||
dh_clean --exclude=Cargo.toml.orig
|
||||
-unlink .cargo
|
||||
-unlink vendor
|
||||
|
||||
override_dh_auto_test:
|
||||
make fish_run_tests
|
||||
|
||||
@@ -23,7 +23,7 @@ If both ``KEYS`` and ``COMMAND`` are given, ``bind`` adds (or replaces) a bindin
|
||||
If only ``KEYS`` is given, any existing binding in the given ``MODE`` will be printed.
|
||||
|
||||
``KEYS`` is a comma-separated list of key names.
|
||||
Modifier keys can be specified by prefixing a key name with a combination of ``ctrl-``, ``alt-`` and ``shift-``.
|
||||
Modifier keys can be specified by prefixing a key name with a combination of ``ctrl-``, ``alt-``, ``shift-`` and ``super-`` (i.e. the "windows" or "command" key).
|
||||
For example, pressing :kbd:`w` while holding the Alt modifier is written as ``alt-w``.
|
||||
Key names are case-sensitive; for example ``alt-W`` is the same as ``alt-shift-w``.
|
||||
``ctrl-x,ctrl-e`` would mean pressing :kbd:`ctrl-x` followed by :kbd:`ctrl-e`.
|
||||
@@ -99,6 +99,12 @@ The following options are available:
|
||||
**-s** or **--silent**
|
||||
Silences some of the error messages, including for unknown key names and unbound sequences.
|
||||
|
||||
**-k KEY_NAME** or **--key KEY_NAME**
|
||||
This looks up KEY_NAME in terminfo and binds that sequence instead of a key that fish would decode.
|
||||
To view a list of the terminfo keys fish knows about, use ``bind --key-names`` or ``bind -K``.
|
||||
This is deprecated and provided for compatibility with older fish versions. You should bind the keys directly.
|
||||
Instead of ``bind -k sright`` use ``bind shift-right``, instead of ``bind -k nul`` use ``bind ctrl-space`` and so on.
|
||||
|
||||
**-h** or **--help**
|
||||
Displays help about using this command.
|
||||
|
||||
|
||||
@@ -52,13 +52,13 @@ Match Glob Examples
|
||||
|
||||
::
|
||||
|
||||
>_ string match '?' a
|
||||
>_ string match 'a' a
|
||||
a
|
||||
|
||||
>_ string match 'a*b' axxb
|
||||
axxb
|
||||
|
||||
>_ string match -i 'a??B' Axxb
|
||||
>_ string match -i 'a*B' Axxb
|
||||
Axxb
|
||||
|
||||
>_ string match -- '-*' -h foo --version bar
|
||||
@@ -67,7 +67,7 @@ Match Glob Examples
|
||||
-h
|
||||
--version
|
||||
|
||||
>_ echo 'ok?' | string match '*\?'
|
||||
>_ echo 'ok?' | string match '*?'
|
||||
ok?
|
||||
|
||||
# Note that only the second STRING will match here.
|
||||
@@ -79,7 +79,7 @@ Match Glob Examples
|
||||
foo
|
||||
foo2
|
||||
|
||||
>_ string match 'foo?' 'foo1' 'foo' 'foo2'
|
||||
>_ string match 'foo*' 'foo1' 'foo' 'foo2'
|
||||
foo1
|
||||
foo2
|
||||
|
||||
|
||||
@@ -10,9 +10,19 @@ import glob
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
from sphinx.highlighting import lexers
|
||||
from sphinx.errors import SphinxWarning
|
||||
from docutils import nodes
|
||||
|
||||
try:
|
||||
import sphinx_markdown_builder
|
||||
|
||||
extensions = [
|
||||
"sphinx_markdown_builder",
|
||||
]
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# -- Helper functions --------------------------------------------------------
|
||||
|
||||
|
||||
@@ -35,11 +45,14 @@ def issue_role(name, rawtext, text, lineno, inliner, options=None, content=None)
|
||||
return [link], []
|
||||
|
||||
|
||||
def remove_fish_indent_lexer(app):
|
||||
if app.builder.name in ("man", "markdown"):
|
||||
del lexers["fish-docs-samples"]
|
||||
|
||||
|
||||
# -- Load our extensions -------------------------------------------------
|
||||
def setup(app):
|
||||
# Our own pygments lexers
|
||||
from sphinx.highlighting import lexers
|
||||
|
||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.insert(0, this_dir)
|
||||
from fish_indent_lexer import FishIndentLexer
|
||||
@@ -52,6 +65,8 @@ def setup(app):
|
||||
app.add_config_value("issue_url", default=None, rebuild="html")
|
||||
app.add_role("issue", issue_role)
|
||||
|
||||
app.connect("builder-inited", remove_fish_indent_lexer)
|
||||
|
||||
|
||||
# The default language to assume
|
||||
highlight_language = "fish-docs-samples"
|
||||
@@ -59,7 +74,7 @@ highlight_language = "fish-docs-samples"
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "fish-shell"
|
||||
copyright = "2024, fish-shell developers"
|
||||
copyright = "fish-shell developers"
|
||||
author = "fish-shell developers"
|
||||
issue_url = "https://github.com/fish-shell/fish-shell/issues"
|
||||
|
||||
@@ -72,7 +87,7 @@ elif "FISH_BUILD_VERSION" in os.environ:
|
||||
ret = os.environ["FISH_BUILD_VERSION"]
|
||||
else:
|
||||
ret = subprocess.check_output(
|
||||
("fish_indent", "--version"), stderr=subprocess.STDOUT
|
||||
("../build_tools/git_version_gen.sh", "--stdout"), stderr=subprocess.STDOUT
|
||||
).decode("utf-8")
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
|
||||
@@ -9,3 +9,5 @@ license = "MIT"
|
||||
[dependencies]
|
||||
libc = "0.2.155"
|
||||
widestring = { version = "1.0.2", optional = true }
|
||||
unicode-segmentation = "1.12.0"
|
||||
unicode-width = "0.2.0"
|
||||
|
||||
@@ -73,7 +73,7 @@ macro_rules! sprintf {
|
||||
/// - `args`: Iterator over the arguments to format.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `Result` which is `Ok` containing the number of characters written on success, or an `Error`.
|
||||
/// A `Result` which is `Ok` containing the width of the string written on success, or an `Error`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
use std::fmt::{self, Write};
|
||||
use std::mem;
|
||||
use std::result::Result;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
#[cfg(feature = "widestring")]
|
||||
use widestring::Utf32Str as wstr;
|
||||
@@ -382,7 +384,7 @@ pub fn sprintf_locale(
|
||||
}
|
||||
|
||||
// Read field width. We do not support $.
|
||||
let width = if s.at(0) == Some('*') {
|
||||
let desired_width = if s.at(0) == Some('*') {
|
||||
let arg_width = args.next().ok_or(Error::MissingArg)?.as_sint()?;
|
||||
s.advance_by(1);
|
||||
if arg_width < 0 {
|
||||
@@ -397,7 +399,7 @@ pub fn sprintf_locale(
|
||||
};
|
||||
|
||||
// Optionally read precision. We do not support $.
|
||||
let mut prec: Option<usize> = if s.at(0) == Some('.') && s.at(1) == Some('*') {
|
||||
let mut desired_precision: Option<usize> = if s.at(0) == Some('.') && s.at(1) == Some('*') {
|
||||
// "A negative precision is treated as though it were missing."
|
||||
// Here we assume the precision is always signed.
|
||||
s.advance_by(2);
|
||||
@@ -410,7 +412,7 @@ pub fn sprintf_locale(
|
||||
None
|
||||
};
|
||||
// Disallow precisions larger than i32::MAX, in keeping with C.
|
||||
if prec.unwrap_or(0) > i32::MAX as usize {
|
||||
if desired_precision.unwrap_or(0) > i32::MAX as usize {
|
||||
return Err(Error::Overflow);
|
||||
}
|
||||
|
||||
@@ -429,7 +431,7 @@ pub fn sprintf_locale(
|
||||
// "If a precision is given with a numeric conversion (d, i, o, u, i, x, and X),
|
||||
// the 0 flag is ignored." p is included here.
|
||||
let spec_is_numeric = matches!(conv_spec, CS::d | CS::u | CS::o | CS::p | CS::x | CS::X);
|
||||
if spec_is_numeric && prec.is_some() {
|
||||
if spec_is_numeric && desired_precision.is_some() {
|
||||
flags.zero_pad = false;
|
||||
}
|
||||
|
||||
@@ -443,13 +445,22 @@ pub fn sprintf_locale(
|
||||
CS::e | CS::f | CS::g | CS::a | CS::E | CS::F | CS::G | CS::A => {
|
||||
// Floating point types handle output on their own.
|
||||
let float = arg.as_float()?;
|
||||
let len = format_float(f, float, width, prec, flags, locale, conv_spec, buf)?;
|
||||
let len = format_float(
|
||||
f,
|
||||
float,
|
||||
desired_width,
|
||||
desired_precision,
|
||||
flags,
|
||||
locale,
|
||||
conv_spec,
|
||||
buf,
|
||||
)?;
|
||||
out_len = out_len.checked_add(len).ok_or(Error::Overflow)?;
|
||||
continue 'main;
|
||||
}
|
||||
CS::p => {
|
||||
const PTR_HEX_DIGITS: usize = 2 * mem::size_of::<*const u8>();
|
||||
prec = prec.map(|p| p.max(PTR_HEX_DIGITS));
|
||||
desired_precision = desired_precision.map(|p| p.max(PTR_HEX_DIGITS));
|
||||
let uint = arg.as_uint()?;
|
||||
if uint != 0 {
|
||||
prefix = "0x";
|
||||
@@ -479,8 +490,8 @@ pub fn sprintf_locale(
|
||||
if uint != 0 {
|
||||
write!(buf, "{:o}", uint)?;
|
||||
}
|
||||
if flags.alt_form && prec.unwrap_or(0) <= buf.len() + 1 {
|
||||
prec = Some(buf.len() + 1);
|
||||
if flags.alt_form && desired_precision.unwrap_or(0) <= buf.len() + 1 {
|
||||
desired_precision = Some(buf.len() + 1);
|
||||
}
|
||||
buf
|
||||
}
|
||||
@@ -514,10 +525,38 @@ pub fn sprintf_locale(
|
||||
CS::s => {
|
||||
// also 'S'
|
||||
let s = arg.as_str(buf)?;
|
||||
let p = prec.unwrap_or(s.len()).min(s.len());
|
||||
prec = Some(p);
|
||||
flags.zero_pad = false;
|
||||
&s[..p]
|
||||
match desired_precision {
|
||||
Some(precision) => {
|
||||
// from man printf(3)
|
||||
// "the maximum number of characters to be printed from a string"
|
||||
// We interpret this to mean the maximum width when printed, as defined by
|
||||
// Unicode grapheme cluster width.
|
||||
let mut byte_len = 0;
|
||||
let mut width = 0;
|
||||
let mut graphemes = s.graphemes(true);
|
||||
// Iteratively add single grapheme clusters as long as the fit within the
|
||||
// width limited by precision.
|
||||
while width < precision {
|
||||
match graphemes.next() {
|
||||
Some(grapheme) => {
|
||||
let grapheme_width = grapheme.width();
|
||||
if width + grapheme_width <= precision {
|
||||
byte_len += grapheme.len();
|
||||
width += grapheme_width;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
let p = precision.min(width);
|
||||
desired_precision = Some(p);
|
||||
&s[..byte_len]
|
||||
}
|
||||
None => s,
|
||||
}
|
||||
}
|
||||
};
|
||||
// Numeric output should be empty iff the value is 0.
|
||||
@@ -528,23 +567,26 @@ pub fn sprintf_locale(
|
||||
// Decide if we want to apply thousands grouping to the body, and compute its size.
|
||||
// Note we have already errored out if grouped is set and this is non-numeric.
|
||||
let wants_grouping = flags.grouped && locale.thousands_sep.is_some();
|
||||
let body_len = match wants_grouping {
|
||||
let body_width = match wants_grouping {
|
||||
// We assume that text representing numbers is ASCII, so len == width.
|
||||
true => body.len() + locale.separator_count(body.len()),
|
||||
false => body.len(),
|
||||
false => body.width(),
|
||||
};
|
||||
|
||||
// Resolve the precision.
|
||||
// In the case of a non-numeric conversion, update the precision to at least the
|
||||
// length of the string.
|
||||
let prec = if !spec_is_numeric {
|
||||
prec.unwrap_or(body_len)
|
||||
let desired_precision = if !spec_is_numeric {
|
||||
desired_precision.unwrap_or(body_width)
|
||||
} else {
|
||||
prec.unwrap_or(1).max(body_len)
|
||||
desired_precision.unwrap_or(1).max(body_width)
|
||||
};
|
||||
|
||||
let prefix_len = prefix.len();
|
||||
let unpadded_width = prefix_len.checked_add(prec).ok_or(Error::Overflow)?;
|
||||
let width = width.max(unpadded_width);
|
||||
let prefix_width = prefix.width();
|
||||
let unpadded_width = prefix_width
|
||||
.checked_add(desired_precision)
|
||||
.ok_or(Error::Overflow)?;
|
||||
let width = desired_width.max(unpadded_width);
|
||||
|
||||
// Pad on the left with spaces to the desired width?
|
||||
if !flags.left_adj && !flags.zero_pad {
|
||||
@@ -560,7 +602,8 @@ pub fn sprintf_locale(
|
||||
}
|
||||
|
||||
// Pad on the left to the given precision?
|
||||
pad(f, '0', prec, body_len)?;
|
||||
// TODO: why pad with 0 here?
|
||||
pad(f, '0', desired_precision, body_width)?;
|
||||
|
||||
// Output the actual value, perhaps with grouping.
|
||||
if wants_grouping {
|
||||
|
||||
@@ -13,6 +13,7 @@ macro_rules! sprintf_check {
|
||||
$(,)? // optional trailing comma
|
||||
) => {
|
||||
{
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
let mut target = String::new();
|
||||
let mut args = [$($arg.to_arg()),*];
|
||||
let len = $crate::printf_c_locale(
|
||||
@@ -20,7 +21,7 @@ macro_rules! sprintf_check {
|
||||
$fmt.as_ref() as &str,
|
||||
&mut args,
|
||||
).expect("printf failed");
|
||||
assert!(len == target.len(), "Wrong length returned: {} vs {}", len, target.len());
|
||||
assert_eq!(len, target.width(), "Wrong length returned");
|
||||
target
|
||||
}
|
||||
};
|
||||
@@ -723,6 +724,18 @@ fn test_huge_precision_g() {
|
||||
sprintf_err!("%.2147483648g", f => Error::Overflow);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_ascii() {
|
||||
assert_fmt!("%3s", "ö" => " ö");
|
||||
assert_fmt!("%3s", "🇺🇳" => " 🇺🇳");
|
||||
assert_fmt!("%.3s", "🇺🇳🇺🇳" => "🇺🇳");
|
||||
assert_fmt!("%.3s", "a🇺🇳" => "a🇺🇳");
|
||||
assert_fmt!("%.3s", "aa🇺🇳" => "aa");
|
||||
assert_fmt!("%3.3s", "aa🇺🇳" => " aa");
|
||||
assert_fmt!("%.1s", "𒈙a" => "𒈙");
|
||||
assert_fmt!("%3.3s", "👨👨👧👧" => " 👨👨👧👧");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_errors() {
|
||||
use Error::*;
|
||||
|
||||
@@ -72,7 +72,7 @@ function __fish_bind_complete
|
||||
printf '%sshift-\tShift modifier…\n' $prefix
|
||||
set -l key_names minus comma backspace delete escape \
|
||||
enter up down left right pageup pagedown home end insert tab \
|
||||
space f(seq 12)
|
||||
space menu printscreen f(seq 12)
|
||||
printf '%s\tNamed key\n' $prefix$key_names
|
||||
end
|
||||
end
|
||||
|
||||
@@ -102,33 +102,36 @@ complete -f -c btrfs -n $restore -s S -l symlink -d 'Restore symbolic links'
|
||||
complete -f -c btrfs -n $restore -s v -l verbose -d Verbose
|
||||
complete -f -c btrfs -n $restore -s i -l ignore-errors -d 'Ignore errors'
|
||||
complete -f -c btrfs -n $restore -s o -l overwrite -d Overwrite
|
||||
complete -f -c btrfs -n $restore -s t -d 'Tree location'
|
||||
complete -f -c btrfs -n $restore -s f -d 'Filesystem location'
|
||||
complete -f -c btrfs -n $restore -s u -l super -d 'Super mirror'
|
||||
complete -f -c btrfs -n $restore -s r -l root -d 'Root objectid'
|
||||
complete -f -c btrfs -n $restore -s t -r -d 'Tree location'
|
||||
complete -f -c btrfs -n $restore -s f -r -d 'Filesystem location'
|
||||
complete -f -c btrfs -n $restore -s u -l super -r -d 'Super mirror'
|
||||
complete -f -c btrfs -n $restore -s r -l root -r -d 'Root objectid'
|
||||
complete -f -c btrfs -n $restore -s d -d 'Find dir'
|
||||
complete -f -c btrfs -n $restore -s l -l list-roots -d 'List tree roots'
|
||||
complete -f -c btrfs -n $restore -s D -l dry-run -d 'Only list files that would be recovered'
|
||||
complete -f -c btrfs -n $restore -l path-regex -d 'Restore only filenames matching regex'
|
||||
complete -f -c btrfs -n $restore -l path-regex -r -d 'Restore only filenames matching regex'
|
||||
complete -f -c btrfs -n $restore -s c -d 'Ignore case (--path-regex only)'
|
||||
|
||||
# btrfs send
|
||||
complete -f -c btrfs -n $send -s e -d ''
|
||||
complete -f -c btrfs -n $send -s p -d 'Send an incremental stream from <parent> to <subvol>'
|
||||
complete -f -c btrfs -n $send -s c -d 'Use this snapshot as a clone source for an incremental send'
|
||||
complete -f -c btrfs -n $send -s f -d 'Output is normally written to stdout'
|
||||
complete -f -c btrfs -n $send -s p -r -d 'Send an incremental stream from <parent> to <subvol>'
|
||||
complete -f -c btrfs -n $send -s c -r -d 'Use this snapshot as a clone source for an incremental send'
|
||||
complete -f -c btrfs -n $send -s f -r -d 'Output is normally written to stdout'
|
||||
complete -f -c btrfs -n $send -l no-data -d 'send in NO_FILE_DATA mode'
|
||||
complete -f -c btrfs -n $send -s v -l verbose -d 'Enable verbose output to stderr'
|
||||
complete -f -c btrfs -n $send -s q -l quiet -d 'Suppress all messages, except errors'
|
||||
complete -f -c btrfs -n $send -l proto -a '0 1 2' -r -d 'Use send protocol version'
|
||||
complete -f -c btrfs -n $send -l proto -l compressed-data -d 'Send compressed data directly'
|
||||
|
||||
# btrfs receive
|
||||
complete -f -c btrfs -n $receive -s v -d 'Increase verbosity about performed actions'
|
||||
complete -f -c btrfs -n $receive -s q -l quiet -d 'Suppress all messages, except errors'
|
||||
complete -f -c btrfs -n $receive -s f -d 'Read the stream from FILE instead of stdin'
|
||||
complete -f -c btrfs -n $receive -s f -r -d 'Read the stream from FILE instead of stdin'
|
||||
complete -f -c btrfs -n $receive -s e -d 'Terminate after receiving an <end cmd> marker in the stream'
|
||||
complete -f -c btrfs -n $receive -s C -l chroot -d 'Confine the process to <mount> using chroot'
|
||||
complete -f -c btrfs -n $receive -s E -l max-errors -d 'Terminate when NUMBER errors occur'
|
||||
complete -f -c btrfs -n $receive -s m -d 'The root mount point of the destination filesystem'
|
||||
complete -f -c btrfs -n $receive -s E -l max-errors -r -d 'Terminate when NUMBER errors occur'
|
||||
complete -f -c btrfs -n $receive -s m -r -d 'The root mount point of the destination filesystem'
|
||||
complete -f -c btrfs -n $receive -l force-decompress -r -d 'Always decompress data'
|
||||
complete -f -c btrfs -n $receive -l dump -d 'Dump stream metadata'
|
||||
|
||||
# btrfs help
|
||||
@@ -147,9 +150,11 @@ complete -f -c btrfs -n $subvolume -a show -d 'Show more information about the s
|
||||
complete -f -c btrfs -n $subvolume -a sync -d 'Wait until given subvolume(s) are completely removed from the filesystem.'
|
||||
# btrfs subvolume create
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume create' -s i -d 'Add subvolume to a qgroup (can be given multiple times)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume create' -s p -d 'Create any missing parent directories'
|
||||
# btrfs subvolume delete
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume delete' -s c -l commit-after -d 'Wait for transaction commit at the end of the operation'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume delete' -s C -l commit-each -d 'Wait for transaction commit after deleting each subvolume'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume delete' -s R -l recursive -d 'Delete subvolumes beneath each subvolume recursively'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume delete' -s v -l verbose -d 'Verbose output of operations'
|
||||
# btrfs subvolume list
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s o -d 'Print only subvolumes below specified path'
|
||||
@@ -164,8 +169,8 @@ complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s s -d 'List on
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s r -d 'List readonly subvolumes (including snapshots)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s d -d 'List deleted subvolumes that are not yet cleaned'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s t -d 'Print the result as a table'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s G -d 'Filter the subvolumes by generation'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s C -d 'Filter the subvolumes by ogeneration'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s G -r -d 'Filter the subvolumes by generation'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -s C -r -d 'Filter the subvolumes by ogeneration'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume list' -l sort -d 'List the subvolume in order' -a '{gen,ogen,rootid,path}'
|
||||
# btrfs subvolume snapshot
|
||||
complete -f -c btrfs -n '__btrfs_command_groups subvolume snapshot' -s r -d 'Create a readonly snapshot'
|
||||
@@ -194,6 +199,7 @@ complete -f -c btrfs -n $filesystem -a defragment -d 'Defragment a file or a dir
|
||||
complete -f -c btrfs -n $filesystem -a resize -d 'Resize a filesystem'
|
||||
complete -f -c btrfs -n $filesystem -a label -d 'Get or change the label of a filesystem'
|
||||
complete -f -c btrfs -n $filesystem -a usage -d 'Show detailed information about internal filesystem usage.'
|
||||
complete -f -c btrfs -n $filesystem -a mkswapfile -d 'Create a new swapfile'
|
||||
# btrfs filesystem df
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem df' -s b -l raw -d 'Show raw numbers in bytes'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem df' -s h -l human-readable -d 'Show human friendly numbers, base 1024'
|
||||
@@ -230,9 +236,12 @@ complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s v -d '
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s r -d 'Defragment files recursively'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s c -d 'Compress the file while defragmenting' -ra '{zlib,lzo,zstd}'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s f -d 'Flush data to disk immediately after defragmenting'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s s -d 'Defragment only from NUMBER byte onward'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s l -d 'Defragment only up to LEN bytes'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s t -d 'Target extent SIZE hint'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s s -r -d 'Defragment only from NUMBER byte onward'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s l -r -d 'Defragment only up to LEN bytes'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s t -r -d 'Target extent SIZE hint'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -l step -r -d 'Defragment in steps of SIZE'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem defragment' -s L -l level -r -d 'Specify compression levels'
|
||||
|
||||
# btrfs filesystem usage
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem usage' -s b -l raw -d 'Show raw numbers in bytes'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem usage' -s h -l human-readable -d 'Show human friendly numbers, base 1024'
|
||||
@@ -244,6 +253,11 @@ complete -f -c btrfs -n '__btrfs_command_groups filesystem usage' -s m -l mbytes
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem usage' -s g -l gbytes -d 'Show sizes in GiB, or GB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem usage' -s t -l tbytes -d 'Show sizes in TiB, or TB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem usage' -s T -d 'Show data in tabular format'
|
||||
# btrfs filesystem mkswapfile
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem mkswapfile' -s s -l size -r -d 'Swapfile size'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem mkswapfile' -s U -l uuid -r -d 'UUID for the swapfile'
|
||||
# btrfs filesystem resize
|
||||
complete -f -c btrfs -n '__btrfs_command_groups filesystem resize' -l enqueue -d 'Wait for other exclusive operations'
|
||||
|
||||
# btrfs balance
|
||||
complete -f -c btrfs -n $balance -a start -d 'Balance chunks across the devices'
|
||||
@@ -252,13 +266,23 @@ complete -f -c btrfs -n $balance -a cancel -d 'Cancel running or paused balance'
|
||||
complete -f -c btrfs -n $balance -a resume -d 'Resume interrupted balance'
|
||||
complete -f -c btrfs -n $balance -a status -d 'Show status of running or paused balance'
|
||||
# btrfs balance start
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s d -d 'Act on data chunks with FILTERS'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s m -d 'Act on metadata chunks with FILTERS'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s s -d 'Act on system chunks with FILTERS (only under -f)'
|
||||
function __btrfs_balance_filters
|
||||
set -l profiles raid{0,1{,c3,c4},10,5,6} dup single
|
||||
set -l btrfs_balance_filters \
|
||||
profiles=$profiles\t"Balances only block groups with the given profiles" \
|
||||
convert=$profiles\t"Convert selected block groups to given profile" \
|
||||
usage= devid= vrange= limit= strips= soft
|
||||
set -l prefix (commandline -tc | string replace -r '^-d' -- '' | string match -rg '^(.*?)?[^,]*$' -- $token)
|
||||
printf "%s\n" "$prefix"$btrfs_balance_filters
|
||||
end
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s d -ra '(__btrfs_balance_filters)' -d 'Act on data chunks with FILTERS'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s m -ra '(__btrfs_balance_filters)' -d 'Act on metadata chunks with FILTERS'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s s -ra '(__btrfs_balance_filters)' -d 'Act on system chunks with FILTERS (only under -f)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s v -d 'Be verbose'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -s f -d 'Force a reduction of metadata integrity'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -l full-balance -d 'Do not print warning and do not delay start'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -l background -l bg -d 'Run the balance as a background process'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance start' -l enqueue -d 'Wait for other exclusive operations'
|
||||
# btrfs balance status
|
||||
complete -f -c btrfs -n '__btrfs_command_groups balance status' -s v -d 'Be verbose'
|
||||
|
||||
@@ -279,6 +303,10 @@ complete -f -c btrfs -n '__btrfs_command_groups device scan' -s u -l forget -d '
|
||||
# btrfs device stats
|
||||
complete -f -c btrfs -n '__btrfs_command_groups device stats' -s c -l check -d 'Return non-zero if any stat counter is not zero'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups device stats' -s z -l reset -d 'Show current stats and reset values to zero'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups device stats' -s T -d "Print stats in a tabular form"
|
||||
# btrfs device remove
|
||||
complete -f -c btrfs -n '__btrfs_command_groups device remove' -l enqueue -d 'Wait for other exclusive operations'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups device remove' -l force -d 'Skip the safety timeout for removing multiple devices'
|
||||
# btrfs device usage
|
||||
complete -f -c btrfs -n '__btrfs_command_groups device usage' -s b -l raw -d 'Show raw numbers in bytes'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups device usage' -s h -l human-readable -d 'Show human friendly numbers, base 1024'
|
||||
@@ -295,12 +323,18 @@ complete -f -c btrfs -n $scrub -a start -d 'Start a new scrub. If a scrub is alr
|
||||
complete -f -c btrfs -n $scrub -a cancel -d 'Cancel a running scrub'
|
||||
complete -f -c btrfs -n $scrub -a resume -d 'Resume previously canceled or interrupted scrub'
|
||||
complete -f -c btrfs -n $scrub -a status -d 'Show status of running or finished scrub'
|
||||
complete -f -c btrfs -n $scrub -a limit -d 'Show or set scrub limits on devices of the given filesystem'
|
||||
# btrfs scrub limit
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub limit' -s d -l devid -d 'Select the device by DEVID to apply the limit'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub limit' -s l -l limit -d 'Set the limit of the device'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub limit' -s a -l all -d 'Apply the limit to all devices'
|
||||
# btrfs scrub start
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s B -d 'Do not background'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s d -d 'Stats per device (-B only)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s q -d 'Be quiet'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s r -d 'Read only mode'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s R -d 'Raw print mode, print full data instead of summary'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -l limit -d 'Set the scrub throughput limit'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s c -d 'Set ioprio class (see ionice(1) manpage)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s n -d 'Set ioprio classdata (see ionice(1) manpage)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups scrub start' -s f -d 'Force starting new scrub'
|
||||
@@ -321,6 +355,9 @@ complete -f -c btrfs -n $rescue -a chunk-recover -d 'Recover the chunk tree by s
|
||||
complete -f -c btrfs -n $rescue -a super-recover -d 'Recover bad superblocks from good copies'
|
||||
complete -f -c btrfs -n $rescue -a zero-log -d 'Clear the tree log. Usable if it\'s corrupted and prevents mount.'
|
||||
complete -f -c btrfs -n $rescue -a fix-device-size -d 'Re-align device and super block sizes'
|
||||
complete -f -c btrfs -n $rescue -a clear-ino-cache -d 'Remove leftover items pertaining to the deprecated inode cache feature'
|
||||
complete -f -c btrfs -n $rescue -a clear-space-cache -d 'Completely remove the on-disk data of free space cache of given version'
|
||||
complete -f -c btrfs -n $rescue -a clear-uuid-tree -d 'Clear the UUID tree'
|
||||
# btrfs rescue chunk-recover
|
||||
complete -f -c btrfs -n '__btrfs_command_groups rescue chunk-recover' -s y -d 'Assume an answer of YES to all questions'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups rescue chunk-recover' -s v -d 'Verbose mode'
|
||||
@@ -337,6 +374,8 @@ complete -f -c btrfs -n $inspect_internal -a min-dev-size -d 'Get the minimum si
|
||||
complete -f -c btrfs -n $inspect_internal -a dump-tree -d 'Dump tree structures from a given device'
|
||||
complete -f -c btrfs -n $inspect_internal -a dump-super -d 'Dump superblock from a device in a textual form'
|
||||
complete -f -c btrfs -n $inspect_internal -a tree-stats -d 'Print various stats for trees'
|
||||
complete -f -c btrfs -n $inspect_internal -a list-chunks -d 'Enumerate chunks on all devices'
|
||||
complete -f -c btrfs -n $inspect_internal -a map-swapfile -d 'Find device-specific physical offset of file that can be used for hibernation'
|
||||
# btrfs inspect-internal inode-resolve
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal inode-resolve' -s v -d 'Verbose mode'
|
||||
# btrfs inspect-internal logical-resolve
|
||||
@@ -351,20 +390,50 @@ complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s d
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s r -l roots -d 'Print only short root node info'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s R -l backups -d 'Print short root node info and backup root info'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s u -l uuid -d 'Print only the uuid tree'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s b -l block -d 'Print info from the specified BLOCK only'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s t -l tree -d 'Print only tree with the given ID'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s b -l block -r -d 'Print info from the specified BLOCK only'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -s t -l tree -r -d 'Print only tree with the given ID'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -l follow -d 'Use with -b, to show all children tree blocks of <block_num>'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -l noscan -d 'Do not scan the devices from the filesystem'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -l bfs -d 'Breadth-first traversal of the trees, print nodes, then leaves'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -l dfs -d 'Depth-first traversal of the trees'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -l hide-names -d 'Print placeholder instead of names'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -l csum-headers -d 'Print b-tree node checksums in headers'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-tree' -l csum-items -d 'Print checksums stored in checksum items'
|
||||
|
||||
# btrfs inspect-internal dump-super
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-super' -s f -l full -d 'Print full superblock information, backup roots etc.'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-super' -s a -l all -d 'Print information about all superblocks'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-super' -s s -l super -d 'Specify which SUPER-BLOCK copy to print out' -ra '{0,1,2}'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-super' -s F -l force -d 'Attempt to dump superblocks with bad magic'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal dump-super' -l bytenr -d 'Specify alternate superblock OFFSET'
|
||||
# btrfs inspect-internal logical-resolve
|
||||
complete -f -c btrfs -n '__btrfs_command_groups logical-resolve' -s P -d 'Print inodes instead of resolving paths'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups logical-resolve' -s o -d 'Ignore offsets, find all references to an extent'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups logical-resolve' -s s -r -d 'Set internal buffer size for storing file names'
|
||||
# btrfs inspect-internal tree-stats
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -s b -d 'Show raw numbers in bytes'
|
||||
# btrfs inspect-internal list-chunks
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l sort -ra 'devid pstart lstart usage length' -d 'Sort by a column (ascending)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l raw -d 'Show raw numbers in bytes'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l human-readable -d 'Show human friendly numbers, base 1024'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l iec -d 'Use 1024 as a base (KiB, MiB, GiB, TiB)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l si -d 'Use 1000 as a base (kB, MB, GB, TB)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l kbytes -d 'Show sizes in KiB, or kB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l mbytes -d 'Show sizes in MiB, or MB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l gbytes -d 'Show sizes in GiB, or GB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal list-chunks' -l tbytes -d 'Show sizes in TiB, or TB with --si'
|
||||
# btrfs inspect-internal map-swapfile
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal map-swapfile' -l resume-offset -s r -d 'Print the value suitable as resume offset for /sys/power/resume_offset.'
|
||||
# btrfs inspect-internal tree-stats
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -s t -r -d 'Print stats only for the given treeid'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l raw -s b -d 'Show raw numbers in bytes'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l human-readable -d 'Show human friendly numbers, base 1024'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l iec -d 'Use 1024 as a base (KiB, MiB, GiB, TiB)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l si -d 'Use 1000 as a base (kB, MB, GB, TB)'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l kbytes -d 'Show sizes in KiB, or kB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l mbytes -d 'Show sizes in MiB, or MB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l gbytes -d 'Show sizes in GiB, or GB with --si'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups inspect-internal tree-stats' -l tbytes -d 'Show sizes in TiB, or TB with --si'
|
||||
|
||||
# btrfs property
|
||||
complete -f -c btrfs -n $property -a get -d 'Get a property value of a btrfs object'
|
||||
@@ -381,9 +450,12 @@ complete -f -c btrfs -n '__btrfs_command_groups property list' -s t -d 'List pro
|
||||
complete -f -c btrfs -n $quota -a enable -d 'Enable subvolume quota support for a filesystem.'
|
||||
complete -f -c btrfs -n $quota -a disable -d 'Disable subvolume quota support for a filesystem.'
|
||||
complete -f -c btrfs -n $quota -a rescan -d 'Trash all qgroup numbers and scan the metadata again with the current config.'
|
||||
# btrfs quota enable
|
||||
complete -f -c btrfs -n '__btrfs_command_groups quota enable' -s s -l simple -d 'Use simple quotas'
|
||||
# btrfs quota rescan
|
||||
complete -f -c btrfs -n '__btrfs_command_groups quota rescan' -s s -d 'Show status of a running rescan operation'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups quota rescan' -s w -d 'Wait for rescan operation to finish'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups quota rescan' -s s -l status -d 'Show status of a running rescan operation'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups quota rescan' -s w -l wait -d 'Wait for rescan operation to finish'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups quota rescan' -s W -l wait-norescan -d 'Wait for rescan to finish without starting it'
|
||||
|
||||
# btrfs qgroup
|
||||
complete -f -c btrfs -n $qgroup -a assign -d 'Assign SRC as the child qgroup of DST'
|
||||
@@ -391,6 +463,7 @@ complete -f -c btrfs -n $qgroup -a remove -d 'Remove a child qgroup SRC from DST
|
||||
complete -f -c btrfs -n $qgroup -a create -d 'Create a subvolume quota group.'
|
||||
complete -f -c btrfs -n $qgroup -a destroy -d 'Destroy a quota group.'
|
||||
complete -f -c btrfs -n $qgroup -a show -d 'Show subvolume quota groups.'
|
||||
complete -f -c btrfs -n $qgroup -a clear-stale -d 'Clear all stale qgroups whose subvolume does not exist'
|
||||
complete -f -c btrfs -n $qgroup -a limit -d 'Set the limits a subvolume quota group.'
|
||||
# btrfs qgroup assign
|
||||
complete -f -c btrfs -n '__btrfs_command_groups qgroup assign' -l rescan -d 'Schedule qutoa rescan if needed'
|
||||
@@ -424,5 +497,7 @@ complete -f -c btrfs -n $replace -a cancel -d 'Cancel a running device replace o
|
||||
complete -f -c btrfs -n '__btrfs_command_groups replace start' -s r -d 'Only read from <srcdev> if no other zero-defect mirror exists'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups replace start' -s f -d 'Force using and overwriting <targetdev>'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups replace start' -s B -d 'Do not background'
|
||||
complete -f -c btrfs -n '__btrfs_command_groups replace start' -l enqueue -d "Wait if there's another exclusive operation running"
|
||||
complete -f -c btrfs -n '__btrfs_command_groups replace start' -s K -l nodiscard -d 'Do not perform TRIM on DEVICES'
|
||||
# btrfs replace status
|
||||
complete -f -c btrfs -n '__btrfs_command_groups replace status' -s 1 -d 'Only print once until the replace operation finishes'
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
## --- WRITTEN MANUALLY ---
|
||||
set -l __fish_cargo_subcommands (cargo --list 2>&1 | string replace -rf '^\s+([^\s]+)\s*(.*)' '$1\t$2' | string escape)
|
||||
|
||||
# Append user-installed extensions (e.g. cargo-foo, invokable as `cargo foo`) to the list of subcommands (à la git)
|
||||
set -la __fish_cargo_subcommands (complete -C'cargo-' | string replace -rf '^cargo-(\w+).*' '$1')
|
||||
|
||||
complete -c cargo -f -c cargo -n __fish_use_subcommand -a "$__fish_cargo_subcommands"
|
||||
complete -c cargo -x -c cargo -n '__fish_seen_subcommand_from help' -a "$__fish_cargo_subcommands"
|
||||
|
||||
@@ -53,27 +50,6 @@ end
|
||||
complete -c cargo -n '__fish_seen_subcommand_from run test build debug check' -l package \
|
||||
-xa "(__fish_cargo_packages)"
|
||||
|
||||
# Look up crates.io crates matching the single argument provided to this function
|
||||
function __fish_cargo_search
|
||||
if test (string length -- "$argv[1]") -le 2
|
||||
# Don't waste time searching for strings with too many results to realistically
|
||||
# provide a meaningful completion within our results limit.
|
||||
return
|
||||
end
|
||||
|
||||
# This doesn't do a prefix search, so bump up the limit a tiny bit to try and
|
||||
# get enough results to show something.
|
||||
cargo search --color never --quiet --limit 20 -- $argv[1] 2>/dev/null |
|
||||
# Filter out placeholders and "... and xxx more crates"
|
||||
string match -rvi '^\.\.\.|= "0.0.0"|# .*(reserved|yanked)' |
|
||||
# Remove the version number and map the description
|
||||
string replace -rf '^([^ ]+).*# (.*)' '$1\t$2'
|
||||
end
|
||||
|
||||
# Complete possible crate names by search the crates.io index
|
||||
complete -c cargo -n '__fish_seen_subcommand_from add install' -n '__fish_is_nth_token 2' \
|
||||
-a "(__fish_cargo_search (commandline -ct))"
|
||||
|
||||
## --- AUTO-GENERATED WITH `cargo complete fish` ---
|
||||
# Manually massaged to improve some descriptions
|
||||
complete -c cargo -n __fish_use_subcommand -l explain -d 'Run `rustc --explain CODE`'
|
||||
|
||||
@@ -49,10 +49,10 @@ function __fish_clj_tools -V bb_helper
|
||||
bb -e "$bb_helper" tools
|
||||
end
|
||||
|
||||
complete -c clj -s X -x -r -k -a "(__fish_complete_list : __fish_clj_aliases)" -d "Use concatenated aliases to modify classpath or supply exec fn/args"
|
||||
complete -c clj -s A -x -r -k -a "(__fish_complete_list : __fish_clj_aliases)" -d "Use concatenated aliases to modify classpath"
|
||||
complete -c clj -s M -x -r -k -a "(__fish_complete_list : __fish_clj_aliases)" -d "Use concatenated aliases to modify classpath or supply main opts"
|
||||
complete -c clj -s T -x -r -k -a "(__fish_complete_list : __fish_clj_tools)" -d "Invoke tool by name or via aliases ala -X"
|
||||
complete -c clj -s X -x -r -k -a "(__fish_stripprefix='^-\w*X' __fish_complete_list : __fish_clj_aliases)" -d "Use concatenated aliases to modify classpath or supply exec fn/args"
|
||||
complete -c clj -s A -x -r -k -a "(__fish_stripprefix='^-\w*A' __fish_complete_list : __fish_clj_aliases)" -d "Use concatenated aliases to modify classpath"
|
||||
complete -c clj -s M -x -r -k -a "(__fish_stripprefix='^-\w*M' __fish_complete_list : __fish_clj_aliases)" -d "Use concatenated aliases to modify classpath or supply main opts"
|
||||
complete -c clj -s T -x -r -k -a "(__fish_stripprefix='^-\w*T' __fish_complete_list : __fish_clj_tools)" -d "Invoke tool by name or via aliases ala -X"
|
||||
|
||||
complete -c clj -f -o Sdeps -r -d "Deps data to use as the last deps file to be merged"
|
||||
complete -c clj -f -o Spath -d "Compute classpath and echo to stdout only"
|
||||
|
||||
@@ -94,3 +94,6 @@ complete -c cryptsetup -l veracrypt-query-pim -d "Query Personal Iteration Multi
|
||||
complete -c cryptsetup -l verbose -s v -d "Shows more detailed error messages"
|
||||
complete -c cryptsetup -l verify-passphrase -s y -d "Verifies the passphrase by asking for it twice"
|
||||
complete -c cryptsetup -l version -s V -d "Print package version"
|
||||
|
||||
# subcommands
|
||||
complete -c cryptsetup -n "__fish_seen_subcommand_from close status resize" -f -r -a "(path basename /dev/mapper/* | string match -v control)"
|
||||
|
||||
@@ -85,7 +85,7 @@ complete -c equery -n '__fish_seen_subcommand_from f files' -s s -l timestamp -d
|
||||
complete -c equery -n '__fish_seen_subcommand_from f files' -s t -l type -d "Include file type in output"
|
||||
complete -c equery -n '__fish_seen_subcommand_from f files' -l tree -d "Display results in a tree"
|
||||
complete -c equery -n '__fish_seen_subcommand_from f files' -s f -l filter -d "Filter output by file type" \
|
||||
-xa "(__fish_complete_list , __fish_equery_files_filter_args)"
|
||||
-xa "(__fish_stripprefix='^(--filter=|-\w*f)' __fish_complete_list , __fish_equery_files_filter_args)"
|
||||
|
||||
# has + hasuse
|
||||
complete -c equery -n '__fish_seen_subcommand_from a has h hasuse' -s I -l exclude-installed -d "Exclude installed pkgs from search path"
|
||||
|
||||
27
share/completions/git-subtree.fish
Normal file
27
share/completions/git-subtree.fish
Normal file
@@ -0,0 +1,27 @@
|
||||
complete -f -c git -a subtree -d 'Manage git subtrees'
|
||||
# Git subtree common completions
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -s q -l quiet -d 'Suppress output'
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -s d -l debug -d 'Debug output'
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -s P -l path -d 'Path to the subtree'
|
||||
|
||||
# Git subtree subcommands
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -a add -d "Add a new subtree to the repository"
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -a merge -d "Merge changes from a subtree into the repository"
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -a split -d "Extract a subtree from the repository"
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -a pull -d "Fetch and integrate changes from a remote subtree"
|
||||
complete -f -c git -n '__fish_git_using_command subtree' -a push -d "Push changes to a remote subtree"
|
||||
|
||||
# Completions for push and split subcommands
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split' -l annotate -d 'Annotate the commit'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split' -s b -l branch -d 'Branch to split'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split' -l ignore-joins -d 'Ignore joins during history reconstruction'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split' -l onto -d 'Specify the commit ID to start history reconstruction'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split' -l rejoin -d 'Merge the synthetic history back into the main project'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split; and __fish_contains_opt rejoin' -l squash -d 'Merge subtree changes as a single commit'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split; and __fish_contains_opt rejoin' -l no-squash -d 'Do not merge subtree changes as a single commit'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from push split; and __fish_contains_opt rejoin' -s m -l message -d 'Use the given message as the commit message for the merge commit'
|
||||
|
||||
# Completion for add and merge subcommands
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from add merge' -l squash -d 'Merge subtree changes as a single commit'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from add merge' -l no-squash -d 'Do not merge subtree changes as a single commit'
|
||||
complete -f -c git -n '__fish_git_using_command subtree; and __fish_seen_subcommand_from add merge' -s m -l message -d 'Use the given message as the commit message for the merge commit'
|
||||
@@ -653,6 +653,7 @@ function __fish_git_aliased_command
|
||||
end
|
||||
end
|
||||
|
||||
set -g __fish_git_aliases
|
||||
git config -z --get-regexp 'alias\..*' | while read -lz alias cmdline
|
||||
set -l command (__fish_git_aliased_command $cmdline)
|
||||
string match -q --regex '\w+' -- $command; or continue
|
||||
@@ -778,7 +779,8 @@ function __fish_git_custom_commands
|
||||
# if any of these completion results match the name of the builtin git commands,
|
||||
# but it's simpler just to blacklist these names. They're unlikely to change,
|
||||
# and the failure mode is we accidentally complete a plumbing command.
|
||||
for name in (string replace -r "^.*/git-([^/]*)" '$1' $PATH/git-*)
|
||||
set -l git_subcommands $PATH/git-*
|
||||
for name in (string replace -r "^.*/git-([^/]*)" '$1' $git_subcommands)
|
||||
switch $name
|
||||
case cvsserver receive-pack shell upload-archive upload-pack
|
||||
# skip these
|
||||
@@ -899,6 +901,15 @@ function __fish_git_is_rebasing
|
||||
test -e (__fish_git rev-parse --absolute-git-dir)/rebase-merge
|
||||
end
|
||||
|
||||
function __fish_git_filters
|
||||
printf "%s\n" \
|
||||
blob:none\t"omits all blobs" \
|
||||
blob:limit=\t"omits blobs by size" \
|
||||
object:type={tag,commit,tree,blob}\t"omit object which are not of the requested type" \
|
||||
sparse:oid=\t"omit blobs not required for a sparse checkout" \
|
||||
tree:\t"omits all blobs and trees"
|
||||
end
|
||||
|
||||
# general options
|
||||
complete git -f -l help -s h -d 'Display manual of a Git command'
|
||||
complete git -f -n __fish_git_needs_command -l version -s v -d 'display git version'
|
||||
@@ -1033,6 +1044,7 @@ complete -f -c git -n '__fish_git_using_command fetch' -l unshallow -d 'Convert
|
||||
complete -f -c git -n '__fish_git_using_command fetch' -l refetch -d 'Re-fetch without negotiating common commits'
|
||||
complete -f -c git -n '__fish_git_using_command fetch' -l negotiation-tip -d 'Only report commits reachable from these tips' -kxa '(__fish_git_commits; __fish_git_branches)'
|
||||
complete -f -c git -n '__fish_git_using_command fetch' -l negotiate-only -d "Don't fetch, only show commits in common with the server"
|
||||
complete -f -c git -n '__fish_git_using_command fetch' -l filter -ra '(__fish_git_filters)' -d 'Request a subset of objects from server'
|
||||
|
||||
# TODO other options
|
||||
|
||||
@@ -1347,6 +1359,7 @@ complete -f -c git -n '__fish_git_using_command clone' -s o -l origin -d 'Use a
|
||||
complete -f -c git -n '__fish_git_using_command clone' -s b -l branch -d 'Use a specific branch instead of the one used by the cloned repository'
|
||||
complete -f -c git -n '__fish_git_using_command clone' -l depth -d 'Truncate the history to a specified number of revisions'
|
||||
complete -f -c git -n '__fish_git_using_command clone' -l recursive -d 'Initialize all submodules within the cloned repository'
|
||||
complete -f -c git -n '__fish_git_using_command clone' -l filter -ra '(__fish_git_filters)' -d 'Partial clone by requesting a subset of objects from server'
|
||||
|
||||
### commit
|
||||
complete -c git -n __fish_git_needs_command -a commit -d 'Record changes to the repository'
|
||||
@@ -1595,6 +1608,7 @@ complete -c git -n '__fish_git_using_command log rev-list' -l bisect
|
||||
complete -c git -n '__fish_git_using_command log rev-list' -l stdin -d 'Read commits from stdin'
|
||||
complete -c git -n '__fish_git_using_command log rev-list' -l cherry-mark -d 'Mark equivalent commits with = and inequivalent with +'
|
||||
complete -c git -n '__fish_git_using_command log rev-list' -l cherry-pick -d 'Omit equivalent commits'
|
||||
complete -f -c git -n '__fish_git_using_command rev-list' -l filter -ra '(__fish_git_filters)' -d 'Omits objects from the list of printed objects'
|
||||
complete -c git -n '__fish_git_using_command log' -l left-only
|
||||
complete -c git -n '__fish_git_using_command log' -l right-only
|
||||
complete -c git -n '__fish_git_using_command log' -l cherry
|
||||
@@ -1798,6 +1812,8 @@ complete -f -c git -n '__fish_git_using_command merge' -l rerere-autoupdate -d '
|
||||
complete -f -c git -n '__fish_git_using_command merge' -l no-rerere-autoupdate -d 'Do not use previous conflict resolutions'
|
||||
complete -f -c git -n '__fish_git_using_command merge' -l abort -d 'Abort the current conflict resolution process'
|
||||
complete -f -c git -n '__fish_git_using_command merge' -l continue -d 'Conclude current conflict resolution process'
|
||||
complete -f -c git -n '__fish_git_using_command merge' -l autostash -d 'Before starting merge, stash local changes, and apply stash when done'
|
||||
complete -f -c git -n '__fish_git_using_command merge' -l no-autostash -d 'Do not stash local changes before starting merge'
|
||||
|
||||
### merge-base
|
||||
complete -f -c git -n __fish_git_needs_command -a merge-base -d 'Find a common ancestor for a merge'
|
||||
@@ -2278,6 +2294,7 @@ complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subco
|
||||
complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subcommand_from update' -s N -l no-fetch -d "Don't fetch new objects from the remote"
|
||||
complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subcommand_from update' -l remote -d "Instead of using superproject's SHA-1, use the state of the submodule's remote-tracking branch"
|
||||
complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subcommand_from update' -l force -d "Discard local changes when switching to a different commit & always run checkout"
|
||||
complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subcommand_from update' -l filter -ra '(__fish_git_filters)' -d 'Request a subset of objects from server'
|
||||
complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subcommand_from add' -l force -d "Also add ignored submodule path"
|
||||
complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subcommand_from deinit' -l force -d "Remove even with local changes"
|
||||
complete -f -c git -n '__fish_git_using_command submodule' -n '__fish_seen_subcommand_from deinit' -l all -d "Remove all submodules"
|
||||
@@ -2571,14 +2588,15 @@ function __fish_git_complete_custom_command -a subcommand
|
||||
set -e cmd[1] # Drop "git".
|
||||
set -l subcommand_args
|
||||
if argparse -s (__fish_git_global_optspecs) -- $cmd
|
||||
set subcommand_args $argv[2..] # Drop the subcommand.
|
||||
set subcommand_args (string escape -- $argv[2..]) # Drop the subcommand.
|
||||
end
|
||||
complete -C "git-$subcommand $(string escape -- $subcommand_args) "(commandline -ct)
|
||||
complete -C "git-$subcommand $subcommand_args "(commandline -ct)
|
||||
end
|
||||
|
||||
# source git-* commands' autocompletion file if exists
|
||||
set -l __fish_git_custom_commands_completion
|
||||
for file in (path filter -xZ $PATH/git-* | path basename)
|
||||
set -l git_subcommands $PATH/git-*
|
||||
for file in (path filter -xZ $git_subcommands | path basename)
|
||||
# Already seen this command earlier in $PATH.
|
||||
contains -- $file $__fish_git_custom_commands_completion
|
||||
and continue
|
||||
|
||||
@@ -4,5 +4,5 @@ complete -c gpasswd -s d -l delete -d 'Remove user from group' -xa '(__fish_comp
|
||||
complete -c gpasswd -s h -l help -d 'Print help'
|
||||
complete -c gpasswd -s r -l remove-password -d 'Remove the GROUP\'s password'
|
||||
complete -c gpasswd -s R -l restrict -d 'Restrict access to GROUP to its members'
|
||||
complete -c gpasswd -s M -l members -d 'Set the list of members of GROUP' -xa '(__fish_complete_list , __fish_complete_users)'
|
||||
complete -c gpasswd -s A -l administrators -d 'set the list of administrators for GROUP' -xa '(__fish_complete_list , __fish_complete_users)'
|
||||
complete -c gpasswd -s M -l members -d 'Set the list of members of GROUP' -xa "(__fish_stripprefix='^(--members=|-\w*M)' __fish_complete_list , __fish_complete_users)"
|
||||
complete -c gpasswd -s A -l administrators -d 'set the list of administrators for GROUP' -xa "(__fish_stripprefix='^(--administrators=|-\w*A)' __fish_complete_list , __fish_complete_users)"
|
||||
|
||||
@@ -36,7 +36,7 @@ complete -c $command -s x -x \
|
||||
-n $compile_condition
|
||||
|
||||
complete -c $command -s W -l warning \
|
||||
-a '(__fish_complete_list , __fish_guild__complete_warnings)' \
|
||||
-a "(__fish_stripprefix='^(--warning=|-\w*W)' __fish_complete_list , __fish_guild__complete_warnings)" \
|
||||
-d 'Specify the warning level for a compilation' \
|
||||
-n $compile_condition
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ complete -c $command -o ds \
|
||||
-d 'Treat the last -s option as if it occurred at this point'
|
||||
|
||||
complete -c $command -l use-srfi \
|
||||
-a '(__fish_complete_list , __fish_guile__complete_srfis)' \
|
||||
-a "(__fish_stripprefix='^--use-srfi=' __fish_complete_list , __fish_guile__complete_srfis)" \
|
||||
-d 'Specify the SRFI modules to load'
|
||||
|
||||
for standard in 6 7
|
||||
|
||||
@@ -58,7 +58,7 @@ complete -c hashcat -l restore -d "Restore session from --session"
|
||||
complete -c hashcat -l restore-disable -d "Do not write restore file"
|
||||
complete -c hashcat -l restore-file-path -rF -d "Specific path to restore file"
|
||||
complete -c hashcat -s o -l outfile -rF -d "Define outfile for recovered hash"
|
||||
complete -c hashcat -l outfile-format -xa "(__fish_complete_list , __fish_hashcat_outfile_formats)" -d "Outfile formats to use"
|
||||
complete -c hashcat -l outfile-format -xa "(__fish_stripprefix='^--outfile-format=' __fish_complete_list , __fish_hashcat_outfile_formats)" -d "Outfile formats to use"
|
||||
complete -c hashcat -l outfile-autohex-disable -d "Disable the use of \$HEX[] in output plains"
|
||||
complete -c hashcat -l outfile-check-timer -x -d "Sets seconds between outfile checks"
|
||||
complete -c hashcat -l wordlist-autohex-disable -d "Disable the conversion of \$HEX[] from the wordlist"
|
||||
@@ -106,7 +106,7 @@ complete -c hashcat -l backend-ignore-metal -d "Do not try to open Metal interfa
|
||||
complete -c hashcat -l backend-ignore-opencl -d "Do not try to open OpenCL interface on startup"
|
||||
complete -c hashcat -s I -l backend-info -d "Show info about detected backend API devices"
|
||||
complete -c hashcat -s d -l backend-devices -x -d "Backend devices to use"
|
||||
complete -c hashcat -s D -l opencl-device-types -xa "(__fish_complete_list , __fish_hashcat_device_types)" -d "OpenCL device-types to use"
|
||||
complete -c hashcat -s D -l opencl-device-types -xa "(__fish_stripprefix='^(--opencl-device-types=|-\w*D)' __fish_complete_list , __fish_hashcat_device_types)" -d "OpenCL device-types to use"
|
||||
complete -c hashcat -s O -l optimized-kernel-enable -d "Enable optimized kernels (limits password length)"
|
||||
complete -c hashcat -s M -l multiply-accel-disable -d "Disable multiply kernel-accel with processor count"
|
||||
complete -c hashcat -s w -l workload-profile -d "Enable a specific workload profile" -xa "
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
jj util completion fish | source
|
||||
# The reason for `__this-command-does-not-exist` is that, if dynamic completion
|
||||
# is not implemented, we'd like to get an error reliably. However, the
|
||||
# behavior of `jj` without arguments depends on the value of a config, see
|
||||
# https://jj-vcs.github.io/jj/latest/config/#default-command
|
||||
if set -l completion (COMPLETE=fish jj __this-command-does-not-exist 2>/dev/null)
|
||||
# jj is new enough for dynamic completions to be implemented
|
||||
printf %s\n $completion | source
|
||||
else
|
||||
jj util completion fish | source
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@ function __fish_john_formats --description "Print JohnTheRipper hash formats"
|
||||
end
|
||||
|
||||
complete -c john -l help -d "print usage summary"
|
||||
complete -c john -l single -fa "(__fish_complete_list , __fish_john_rules)" -d "single crack mode"
|
||||
complete -c john -l single -fa "(__fish_stripprefix='^--single=' __fish_complete_list , __fish_john_rules)" -d "single crack mode"
|
||||
complete -c john -l single-seed -rf -d "add static seed word(s) for all salts in single mode"
|
||||
complete -c john -l single-wordlist -rF -d "short wordlist with static seed words/morphemes"
|
||||
complete -c john -l single-user-seed -rF -d "wordlist with seeds per username"
|
||||
@@ -35,8 +35,8 @@ complete -c john -l prince-case-permute -d "permute case of first letter"
|
||||
complete -c john -l prince-mmap -d "memory-map infile"
|
||||
complete -c john -l prince-keyspace -d "just show total keyspace that would be produced"
|
||||
complete -c john -l encoding -l input-encoding -fa "$__fish_john_encodings" -d "input encoding"
|
||||
complete -c john -l rules -fa "(__fish_complete_list , __fish_john_rules)" -d "enable word mangling rules"
|
||||
complete -c john -l rules-stack -fa "(__fish_complete_list , __fish_john_rules)" -d "stacked rules"
|
||||
complete -c john -l rules -fa "(__fish_stripprefix='^--rules=' __fish_complete_list , __fish_john_rules)" -d "enable word mangling rules"
|
||||
complete -c john -l rules-stack -fa "(__fish_stripprefix='^--rules-stack=' __fish_complete_list , __fish_john_rules)" -d "stacked rules"
|
||||
complete -c john -l rules-skip-nop -d "skip any NOP rules"
|
||||
complete -c john -l incremental -fa "(john --list=inc-modes 2>/dev/null)" -d "incremental mode"
|
||||
complete -c john -l incremental-charcount -rf -d "override CharCount for incremental mode"
|
||||
@@ -97,4 +97,4 @@ complete -c john -l internal-codepage -fa "$__fish_john_encodings" -d "codepage
|
||||
complete -c john -l target-encoding -fa "$__fish_john_encodings" -d "output encoding"
|
||||
complete -c john -l tune -fa "auto report N" -d "tuning options"
|
||||
complete -c john -l force-tty -d "set up terminal for reading keystrokes"
|
||||
complete -c john -l format -fa "(__fish_complete_list , __fish_john_formats)" -d "force hash type"
|
||||
complete -c john -l format -fa "(__fish_stripprefix='^--format=' __fish_complete_list , __fish_john_formats)" -d "force hash type"
|
||||
|
||||
@@ -41,7 +41,7 @@ complete -c losetup -s v -l verbose -d "Verbose mode"
|
||||
complete -c losetup -s J -l json -d "Use JSON --list output format"
|
||||
complete -c losetup -s l -l list -d "List info about all or specified"
|
||||
complete -c losetup -s n -l noheadings -d "Don't print headings for --list output"
|
||||
complete -c losetup -s O -l output -x -a "(__fish_complete_list , __fish_print_losetup_list_output)" -d "Specify columns to output for --list"
|
||||
complete -c losetup -s O -l output -x -a "(__fish_stripprefix='^(--output=|-\w*O)' __fish_complete_list , __fish_print_losetup_list_output)" -d "Specify columns to output for --list"
|
||||
complete -c losetup -l output-all -d "Output all columns"
|
||||
complete -c losetup -l raw -d "Use raw --list output format"
|
||||
complete -c losetup -s h -l help -d "Display help"
|
||||
|
||||
@@ -25,7 +25,7 @@ complete -c lpadmin -s o -xa printer-is-shared=true -d 'Sets dest to shared/publ
|
||||
complete -c lpadmin -s o -xa printer-is-shared=false -d 'Sets dest to shared/published or unshared/unpublished'
|
||||
complete -c lpadmin -s o -d 'Set IPP operation policy associated with dest' -xa "printer-policy=(test -r /etc/cups/cupsd.conf; and string replace -r --filter '<Policy (.*)>' '$1' < /etc/cups/cupsd.conf)"
|
||||
|
||||
complete -c lpadmin -s u -xa 'allow:all allow:none (__fish_complete_list , __fish_complete_users allow:)' -d 'Sets user-level access control on a destination'
|
||||
complete -c lpadmin -s u -xa '(__fish_complete_list , __fish_complete_groups allow: @)' -d 'Sets user-level access control on a destination'
|
||||
complete -c lpadmin -s u -xa 'deny:all deny:none (__fish_complete_list , __fish_complete_users deny:)' -d 'Sets user-level access control on a destination'
|
||||
complete -c lpadmin -s u -xa '(__fish_complete_list , __fish_complete_groups deny: @)' -d 'Sets user-level access control on a destination'
|
||||
complete -c lpadmin -s u -xa "allow:all allow:none (__fish_stripprefix='^-\w*u' __fish_complete_list , __fish_complete_users allow:)" -d 'Sets user-level access control on a destination'
|
||||
complete -c lpadmin -s u -xa "(__fish_stripprefix='^-\w*u' __fish_complete_list , __fish_complete_groups allow: @)" -d 'Sets user-level access control on a destination'
|
||||
complete -c lpadmin -s u -xa "deny:all deny:none (__fish_stripprefix='^-\w*u' __fish_complete_list , __fish_complete_users deny:)" -d 'Sets user-level access control on a destination'
|
||||
complete -c lpadmin -s u -xa "(__fish_stripprefix='^-\w*u' __fish_complete_list , __fish_complete_groups deny: @)" -d 'Sets user-level access control on a destination'
|
||||
|
||||
@@ -12,7 +12,7 @@ complete -c lsblk -s h -l help -d "usage information (this)"
|
||||
complete -c lsblk -s i -l ascii -d "use ascii characters only"
|
||||
complete -c lsblk -s m -l perms -d "output info about permissions"
|
||||
complete -c lsblk -s n -l noheadings -d "don't print headings"
|
||||
complete -c lsblk -s o -l output -d "output columns" -xa '( __fish_complete_list , __fish_print_lsblk_columns )'
|
||||
complete -c lsblk -s o -l output -d "output columns" -xa "(__fish_stripprefix='^(--output=|-\w*o)' __fish_complete_list , __fish_print_lsblk_columns)"
|
||||
complete -c lsblk -s P -l pairs -d "use key='value' output format"
|
||||
complete -c lsblk -s r -l raw -d "use raw output format"
|
||||
complete -c lsblk -s t -l topology -d "output info about topology"
|
||||
|
||||
@@ -11,9 +11,9 @@ i\t"ignore the device cache file"
|
||||
r\t"read the device cache file"
|
||||
u\t"read and update the device cache file"'
|
||||
|
||||
complete -c lsof -s g -d 'select by group (^ - negates)' -xa '(__fish_complete_list , __fish_complete_groups)'
|
||||
complete -c lsof -s g -d 'select by group (^ - negates)' -xa "(__fish_stripprefix='^-\w*g' __fish_complete_list , __fish_complete_groups)"
|
||||
complete -c lsof -s l -d 'Convert UIDs to login names'
|
||||
complete -c lsof -s p -d 'Select or exclude processes by pid' -xa '(__fish_complete_list , __fish_complete_pids)'
|
||||
complete -c lsof -s p -d 'Select or exclude processes by pid' -xa "(__fish_stripprefix='^-\w*p' __fish_complete_list , __fish_complete_pids)"
|
||||
complete -c lsof -s R -d 'Print PPID'
|
||||
complete -c lsof -s t -d 'Produce terse output (pids only, no header)'
|
||||
complete -c lsof -s u -d 'select by user (^ - negates)' -xa '(__fish_complete_list , __fish_complete_users)'
|
||||
complete -c lsof -s u -d 'select by user (^ - negates)' -xa "(__fish_stripprefix='^-\w*u' __fish_complete_list , __fish_complete_users)"
|
||||
|
||||
@@ -35,7 +35,7 @@ function __fish_complete_openssl_ciphers
|
||||
printf "%s\tCipher String\n" $cs
|
||||
end
|
||||
end
|
||||
complete -c ncat -l ssl-ciphers -x -a "(__fish_complete_list : __fish_complete_openssl_ciphers)" -d "Specify SSL ciphersuites"
|
||||
complete -c ncat -l ssl-ciphers -x -a "(__fish_stripprefix='^--ssl-ciphers=' __fish_complete_list : __fish_complete_openssl_ciphers)" -d "Specify SSL ciphersuites"
|
||||
complete -c ncat -l ssl-servername -x -a "(__fish_print_hostnames)" -d "Request distinct server name"
|
||||
complete -c ncat -l ssl-alpn -x -d "Specify ALPN protocol list"
|
||||
|
||||
|
||||
@@ -92,11 +92,11 @@ function __fish_complete_nmap_script
|
||||
end
|
||||
echo -e $__fish_nmap_script_completion_cache
|
||||
end
|
||||
complete -c nmap -l script -r -a "(__fish_complete_list , __fish_complete_nmap_script)"
|
||||
complete -c nmap -l script -r -a "(__fish_stripprefix='^--script=' __fish_complete_list , __fish_complete_nmap_script)"
|
||||
complete -c nmap -l script -r -d 'Runs a script scan'
|
||||
complete -c nmap -l script-args -d 'provide arguments to NSE scripts'
|
||||
complete -c nmap -l script-args-file -r -d 'load arguments to NSE scripts from a file'
|
||||
complete -c nmap -l script-help -r -a "(__fish_complete_list , __fish_complete_nmap_script)"
|
||||
complete -c nmap -l script-help -r -a "(__fish_stripprefix='^--script-help=' __fish_complete_list , __fish_complete_nmap_script)"
|
||||
complete -c nmap -l script-help -r -d "Shows help about scripts"
|
||||
complete -c nmap -l script-trace
|
||||
complete -c nmap -l script-updatedb
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
set -l nmoutput (nmcli -g NAME connection show --active 2>/dev/null)
|
||||
or exit # networkmanager isn't running, no point in completing
|
||||
set -l cname (string escape -- $nmoutput\t"Active connection")
|
||||
set -a cname (string escape -- (nmcli -g NAME connection show)\t"Connection")
|
||||
set -l ifname (string escape -- (nmcli -g DEVICE device status)\t"Interface name")
|
||||
set -l ssid (string escape -- (nmcli -g SSID device wifi list)\t"SSID")
|
||||
set -l bssid (string escape -- (nmcli -g BSSID device wifi list | string replace --all \\ '')\t"BSSID")
|
||||
set -l nmoutput '(nmcli -g NAME connection show --active 2>/dev/null)'
|
||||
set -l cname "$nmoutput"\t"Active connection"
|
||||
set -a cname '(nmcli -g NAME connection show 2>/dev/null)\t"Connection"'
|
||||
set -l ifname '(nmcli -g DEVICE device status 2>/dev/null)\t"Interface name"'
|
||||
set -l ssid '(nmcli -g SSID device wifi list 2>/dev/null)\t"SSID"'
|
||||
set -l bssid '(nmcli -g BSSID device wifi list 2>/dev/null | string replace --all \\\ "")\t"BSSID"'
|
||||
|
||||
set -l nmcli_commands general networking radio connection device agent monitor help
|
||||
set -l nmcli_general status hostname permissions logging help
|
||||
|
||||
@@ -10,7 +10,7 @@ if test "$gnu_linux" -eq 1
|
||||
# Some short options are GNU-only
|
||||
complete -c ps -s a -d "Select all processes except session leaders and terminal-less"
|
||||
complete -c ps -s A -d "Select all"
|
||||
complete -c ps -s C -d "Select by command" -ra '(__fish_complete_list , __fish_complete_proc)'
|
||||
complete -c ps -s C -d "Select by command" -ra "(__fish_stripprefix='^-\w*C' __fish_complete_list , __fish_complete_proc)"
|
||||
complete -c ps -s c -d 'Show different scheduler information for the -l option'
|
||||
complete -c ps -s d -d "Select all processes except session leaders"
|
||||
complete -c ps -s e -d "Select all"
|
||||
@@ -24,9 +24,9 @@ if test "$gnu_linux" -eq 1
|
||||
complete -c ps -s m -d 'Show threads after processes'
|
||||
complete -c ps -s N -d "Invert selection"
|
||||
complete -c ps -s n -d "Set namelist file" -r
|
||||
complete -c ps -s s -l sid -d "Select by session ID" -x -a "(__fish_complete_list , __fish_complete_pids)"
|
||||
complete -c ps -s s -l sid -d "Select by session ID" -x -a "(__fish_stripprefix='^(--sid=|-\w*s)' __fish_complete_list , __fish_complete_pids)"
|
||||
complete -c ps -s T -d "Show threads. With SPID"
|
||||
complete -c ps -s u -l user -d "Select by user" -x -a "(__fish_complete_list , __fish_complete_users)"
|
||||
complete -c ps -s u -l user -d "Select by user" -x -a "(__fish_stripprefix='^(--script=|-\w*u)' __fish_complete_list , __fish_complete_users)"
|
||||
complete -c ps -s V -l version -d "Display version and exit"
|
||||
complete -c ps -s y -d "Do not show flags"
|
||||
|
||||
@@ -39,7 +39,7 @@ if test "$gnu_linux" -eq 1
|
||||
complete -c ps -l info -d "Display debug info"
|
||||
complete -c ps -l lines -l rows -d "Set screen height" -r
|
||||
complete -c ps -l no-headers -d 'Print no headers'
|
||||
complete -c ps -l ppid -d "Select by parent PID" -x -a "(__fish_complete_list , __fish_complete_pids)"
|
||||
complete -c ps -l ppid -d "Select by parent PID" -x -a "(__fish_stripprefix='^--ppid=' __fish_complete_list , __fish_complete_pids)"
|
||||
complete -c ps -l sort -d 'Specify sort order' -r
|
||||
else
|
||||
# Assume BSD options otherwise
|
||||
@@ -81,6 +81,6 @@ end
|
||||
complete -c ps -s o -lformat$bsd_null -d "User defined format" -x
|
||||
complete -c ps -s Z -lcontext$bsd_null -d "Include security info"
|
||||
complete -c ps -s t -ltty$bsd_null -d "Select by tty" -r
|
||||
complete -c ps -s G -lgroup$bsd_null -d "Select by group" -x -a "(__fish_complete_list , __fish_complete_groups)"
|
||||
complete -c ps -s U -luser$bsd_null -d "Select by user" -x -a "(__fish_complete_list , __fish_complete_users)"
|
||||
complete -c ps -s p -lpid$bsd_null -d "Select by PID" -x -a "(__fish_complete_list , __fish_complete_pids)"
|
||||
complete -c ps -s G -lgroup$bsd_null -d "Select by group" -x -a "(__fish_stripprefix='^(--group=|-\w*G)' __fish_complete_list , __fish_complete_groups)"
|
||||
complete -c ps -s U -luser$bsd_null -d "Select by user" -x -a "(__fish_stripprefix='^(--user=|-\w*U)' __fish_complete_list , __fish_complete_users)"
|
||||
complete -c ps -s p -lpid$bsd_null -d "Select by PID" -x -a "(__fish_stripprefix='^(--pid=|-\w*p)' __fish_complete_list , __fish_complete_pids)"
|
||||
|
||||
@@ -15,7 +15,7 @@ complete -c setxkbmap -o keycodes -d 'Specifies keycodes component name' -xa "(s
|
||||
complete -c setxkbmap -o keymap -d 'Specifies name of keymap to load' -xa "(sed -r $filter /usr/share/X11/xkb/keymap.dir)"
|
||||
complete -c setxkbmap -o layout -d 'Specifies layout used to choose component names' -xa "(__fish_complete_setxkbmap layout)"
|
||||
complete -c setxkbmap -o model -d 'Specifies model used to choose component names' -xa "(__fish_complete_setxkbmap model)"
|
||||
complete -c setxkbmap -o option -d 'Adds an option used to choose component names' -xa "(__fish_complete_list , '__fish_complete_setxkbmap option')"
|
||||
complete -c setxkbmap -o option -d 'Adds an option used to choose component names' -xa "(__fish_stripprefix='^--option=' __fish_complete_list , '__fish_complete_setxkbmap option')"
|
||||
complete -c setxkbmap -o print -d 'Print a complete xkb_keymap description and exit'
|
||||
complete -c setxkbmap -o query -d 'Print the current layout settings and exit'
|
||||
complete -c setxkbmap -o rules -d 'Name of rules file to use' -x
|
||||
|
||||
@@ -25,7 +25,7 @@ complete -c ssh -s k -d "Disables forwarding of GSSAPI credentials"
|
||||
complete -c ssh -s L -d "Specify local port forwarding" -x
|
||||
complete -c ssh -s l -x -a "(__fish_complete_users)" -d User
|
||||
complete -c ssh -s M -d "Places the ssh client into master mode"
|
||||
complete -c ssh -s m -d "MAC algorithm" -xa "(__fish_complete_list , __fish_ssh_macs)"
|
||||
complete -c ssh -s m -d "MAC algorithm" -xa "(__fish_stripprefix='^-\w*m' __fish_complete_list , __fish_ssh_macs)"
|
||||
complete -c ssh -s N -d "Do not execute remote command"
|
||||
complete -c ssh -s n -d "Prevent reading from stdin"
|
||||
complete -c ssh -s O -d "Control an active connection multiplexing master process" -x
|
||||
|
||||
@@ -12,6 +12,6 @@ complete -c su -s G -l supp-group -x -a "(__fish_complete_groups)" -d "Specify a
|
||||
complete -c su -s m -s p -l preserve_environment -d "Preserve environment"
|
||||
complete -c su -s P -l pty -d "Create pseudo-terminal for the session"
|
||||
complete -c su -s s -l shell -x -a "(cat /etc/shells)" -d "Run the specified shell"
|
||||
complete -c su -s w -l whitelist-environment -x -a "(__fish_complete_list , __fish_complete_su_env_whitelist)" -d "Don't reset these environment variables"
|
||||
complete -c su -s w -l whitelist-environment -x -a "(__fish_stripprefix='^(--whitelist-environment=|-\w*w)' __fish_complete_list , __fish_complete_su_env_whitelist)" -d "Don't reset these environment variables"
|
||||
complete -c su -s h -l help -d "Display help and exit"
|
||||
complete -c su -s V -l version -d "Display version and exit"
|
||||
|
||||
@@ -16,9 +16,18 @@ complete -c systemd-analyze -l to-pattern -d 'dot: show relationships matching r
|
||||
complete -c systemd-analyze -l fuzz -x -d 'critical-chain: also show units which finished timespan earlier than last unit in same level'
|
||||
complete -c systemd-analyze -l man -xa no -d 'Do not invoke man to verify the existence of man pages'
|
||||
complete -c systemd-analyze -l generators -d 'Invoke unit generators'
|
||||
complete -c systemd-analyze -l instance -r -d 'Fallback instance name for template units'
|
||||
complete -c systemd-analyze -l root -xa "(__fish_complete_directories)" -d 'With cat-files, show config files underneath the specified root path'
|
||||
complete -c systemd-analyze -l image -r -d 'With cat-files, show config files inside the specified image path'
|
||||
complete -c systemd-analyze -l image-policy -d 'Disk image dissection policy'
|
||||
complete -c systemd-analyze -l iterations -x -d 'calendar: show number of iterations the calendar expression will elapse next'
|
||||
complete -c systemd-analyze -l base-time -x -d 'calendar: show next iterations relative to the specified point in time'
|
||||
complete -c systemd-analyze -l tldr -d 'cat-config: skip comments, empty lines and section headers'
|
||||
complete -c systemd-analyze -l unit -r -d "condition: evaluate Condition and Assert assignments in unit file"
|
||||
complete -c systemd-analyze -l table -d 'plot: output raw time data in a table'
|
||||
complete -c systemd-analyze -l no-legend -d 'plot: exclude legends/hints'
|
||||
complete -c systemd-analyze -l detailed -d "plot: show activation timestamps details in SVG plot"
|
||||
complete -c systemd-analyze -l scale-svg -r -d "plot: stretch the x-axis of the plot"
|
||||
complete -c systemd-analyze -s H -l host -xa "(__fish_complete_user_at_hosts)" -d 'Execute the operation on a remote host'
|
||||
complete -c systemd-analyze -s M -l machine -xa "(__fish_systemd_machines)" -d 'Execute operation on a VM or container'
|
||||
complete -c systemd-analyze -s h -l help -d 'Print a short help and exit'
|
||||
@@ -34,6 +43,7 @@ complete -c systemd-analyze -n __fish_use_subcommand -a critical-chain -d "Print
|
||||
complete -c systemd-analyze -n "__fish_seen_subcommand_from critical-chain" -a "(__fish_systemd_units)"
|
||||
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a dump -d "Output serialization of server state"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a malloc -d "Output internal memory state of D-Bus service"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a plot -d "Output SVG graphic showing service initialization"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a dot -d "Output dependency graph in dot(1) format"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a unit-paths -d "List all directories from which unit files may be loaded"
|
||||
@@ -41,15 +51,25 @@ complete -c systemd-analyze -n __fish_use_subcommand -a exit-status -d "List exi
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a capability -d "List Linux capabilities along with their numeric IDs"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a condition -d "Evaluate Condition and Assert assignments"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a syscall-filter -d "List system calls contained in the specified system call set"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a filesystems -d "List filesystems"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a calendar -d "Normalize repetitive calendar events and calculate when they elapse next"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a timestamp -d "Parse timestamp and output the normalized form"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a timestamp -d "Parse time span and output the normalized form"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a timespan -d "Parse time span and output the normalized form"
|
||||
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a cat-config -d "Show contents of a config file"
|
||||
complete -c systemd-analyze -n "__fish_seen_subcommand_from cat-config" -F
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a compare-versions -d "Compare two version strings"
|
||||
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a verify -d "Check unit files for correctness"
|
||||
complete -c systemd-analyze -n "__fish_seen_subcommand_from verify" -F
|
||||
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a security -d "Analyze security settings of specified service units"
|
||||
complete -c systemd-analyze -n "__fish_seen_subcommand_from security" -a "(__fish_systemctl_services)"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a inspect-elf -d "Parse and print ELF object packaging metadata"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a fdstore -d "List contents of service unit's file descriptor store"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a image-policy -d "Analyze image policy string"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a has-tpm2 -d "Report TPM2 support"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a pcrs -d "Show known TPM2 PCRs"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a srk -d "Read Storage Root Key from TPM2 device"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a architectures -d "List known CPU architectures"
|
||||
complete -c systemd-analyze -n __fish_use_subcommand -a smbios11 -d "Show SMBIOS Type #11 strings passed to the system"
|
||||
|
||||
@@ -40,6 +40,6 @@ complete -c systemd-cryptenroll -l fido2-with-user-presence -xa "yes no" -d "Req
|
||||
complete -c systemd-cryptenroll -l fido2-with-user-verification -xa "yes no" -d "Require user verification when unlocking the volume"
|
||||
complete -c systemd-cryptenroll -l tpm2-device -kxa "(__fish_cryptenroll_tpm2_devices)" -d "Enroll a TPM2 security chip"
|
||||
complete -c systemd-cryptenroll -l tpm2-pcrs -x -d "Bind the enrollment of TPM2 device to speficied PCRs"
|
||||
complete -c systemd-cryptenroll -l wipe-slot -kxa "(__fish_complete_list , __fish_cryptenroll_complete_wipe)" -d "Wipes one or more LUKS2 key slots"
|
||||
complete -c systemd-cryptenroll -l wipe-slot -kxa "(__fish_stripprefix='^--wipe-slot=' __fish_complete_list , __fish_cryptenroll_complete_wipe)" -d "Wipes one or more LUKS2 key slots"
|
||||
complete -c systemd-cryptenroll -l help -s h -d "Print a short help"
|
||||
complete -c systemd-cryptenroll -l version -d "Print a short version string"
|
||||
|
||||
@@ -5,7 +5,7 @@ complete -c usermod -s d -l home -d "Change user's login directory" -r
|
||||
complete -c usermod -s e -l expiredate -d "Date (YYYY-MM-DD) on which the user account will be disabled" -x
|
||||
complete -c usermod -s f -l inactive -d "Number of days after a password expires until the account is locked" -xa "(seq 0 365)"
|
||||
complete -c usermod -s g -l gid -d "Group name or number of the user's new initial login group" -xa "(__fish_complete_groups)"
|
||||
complete -c usermod -s G -l groups -d "List of groups which the user is also a member of" -xa "(__fish_complete_list , __fish_complete_groups)"
|
||||
complete -c usermod -s G -l groups -d "List of groups which the user is also a member of" -xa "(__fish_stripprefix='^(--groups=|-\w*G)' __fish_complete_list , __fish_complete_groups)"
|
||||
complete -c usermod -s l -l login -d "Change user's name" -x
|
||||
complete -c usermod -s L -l lock -d "Lock user's password" -f
|
||||
complete -c usermod -s m -l move-home -d "Move the content of the user's home directory to the new location" -f
|
||||
|
||||
58
share/completions/wlr-randr.fish
Normal file
58
share/completions/wlr-randr.fish
Normal file
@@ -0,0 +1,58 @@
|
||||
# `wlr-randr` completions.
|
||||
# See: https://gitlab.freedesktop.org/emersion/wlr-randr
|
||||
|
||||
function __fish_print_wlr-randr_outputs --argument-names exclude
|
||||
if command -q jq
|
||||
wlr-randr --json | jq \
|
||||
--raw-output \
|
||||
--arg exclude "$exclude" \
|
||||
'.[] | select(.name != $exclude) | "\(.name)\t\(.make), \(.model)"'
|
||||
end
|
||||
end
|
||||
|
||||
function __fish_get_wlr-randr-current_output
|
||||
set -l last_output
|
||||
set -l tokens (commandline -xpc)
|
||||
|
||||
while set -q tokens[1]
|
||||
if test "$tokens[1]" = --output
|
||||
set last_output "$tokens[2]"
|
||||
set -e tokens[1]
|
||||
else if string match -qr -- '^--output=' "$tokens[1]"
|
||||
set last_output (string replace -r -- '--output=(.*)' '$1' "$tokens[1]")
|
||||
end
|
||||
|
||||
set -e tokens[1]
|
||||
end
|
||||
|
||||
printf "%s" $last_output
|
||||
end
|
||||
|
||||
function __fish_complete_wlr-randr_modes
|
||||
if command -q jq
|
||||
set -l output (__fish_get_wlr-randr-current_output)
|
||||
|
||||
wlr-randr --json | jq \
|
||||
--raw-output \
|
||||
--arg output "$output" \
|
||||
--arg preferred_str Preferred \
|
||||
--arg empty_str '' \
|
||||
'.[] | select(.name == $output) | .modes[] | "\(.width)x\(.height)\t\(if .preferred then $preferred_str else $empty_str end)"'
|
||||
end
|
||||
end
|
||||
|
||||
complete -c wlr-randr -f
|
||||
complete -c wlr-randr -s h -l help -d 'Show help'
|
||||
complete -c wlr-randr -l json -d 'Print as JSON'
|
||||
complete -c wlr-randr -l dryrun -d 'Dry run'
|
||||
complete -c wlr-randr -l output -x -d Output -a '(__fish_print_wlr-randr_outputs)'
|
||||
complete -c wlr-randr -l on -d 'Turn on'
|
||||
complete -c wlr-randr -l off -d 'Turn off'
|
||||
complete -c wlr-randr -l mode -x -d Mode -a '(__fish_complete_wlr-randr_modes)'
|
||||
complete -c wlr-randr -l pos -r -d Position
|
||||
complete -c wlr-randr -l left-of -x -d 'Relative left position' -a '(__fish_print_wlr-randr_outputs (__fish_get_wlr-randr-current_output))'
|
||||
complete -c wlr-randr -l right-of -x -d 'Relative right position' -a '(__fish_print_wlr-randr_outputs (__fish_get_wlr-randr-current_output))'
|
||||
complete -c wlr-randr -l above -x -d 'Relative top position' -a '(__fish_print_wlr-randr_outputs (__fish_get_wlr-randr-current_output))'
|
||||
complete -c wlr-randr -l below -x -d 'Relative bottom position' -a '(__fish_print_wlr-randr_outputs (__fish_get_wlr-randr-current_output))'
|
||||
complete -c wlr-randr -l transform -x -d Transformation -a 'normal\t 90\t 180\t 270\t flipped\t flipped-90\t flipped-180\t flipped-270\t'
|
||||
complete -c wlr-randr -l scale -x -d Scale
|
||||
@@ -50,7 +50,7 @@ complete -c $progname -s d -d 'Enable extra debugging shown to stderr'
|
||||
complete -c $progname -s h -d 'Show the help message'
|
||||
complete -c $progname -s i -d 'Ignore repositories defined in configuration files'
|
||||
complete -c $progname -s M -d 'For remote repositories, the data is fetched and stored in memory only'
|
||||
complete -c $progname -s p -d 'Match one or more package properties' -xa "(__fish_complete_list , __fish_print_xbps_pkg_props)"
|
||||
complete -c $progname -s p -d 'Match one or more package properties' -xa "(__fish_stripprefix='^-\w*p' __fish_complete_list , __fish_print_xbps_pkg_props)"
|
||||
complete -c $progname -s R -d 'Enable repository mode'
|
||||
complete -c $progname -l repository -d 'Append the specified repository to the top of the list'
|
||||
complete -c $progname -l regex -d 'Use Extended Regular Expressions'
|
||||
|
||||
@@ -14,15 +14,17 @@ where:
|
||||
set -q prefix[1]
|
||||
or set -l prefix ""
|
||||
set -l pat "$(commandline -t)"
|
||||
#set -l pat $argv[5]
|
||||
if set -q __fish_stripprefix[1]
|
||||
set pat "$(string replace -r -- "$__fish_stripprefix" "" $pat)"
|
||||
end
|
||||
switch $pat
|
||||
case "*$div*"
|
||||
for i in (echo $pat | sed "s/^\(.\+$div\)$iprefix.*\$/\1/")$iprefix(eval $cmd)
|
||||
string unescape -- $i
|
||||
for i in (string unescape -- $pat | sed "s/^\(.\+$div\)$iprefix.*\$/\1/")$iprefix(eval $cmd)
|
||||
printf %s\n $i
|
||||
end
|
||||
case '*'
|
||||
for i in $prefix$iprefix(eval $cmd)
|
||||
string unescape -- $i
|
||||
printf %s\n $i
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
function __fish_complete_pgrep -d 'Complete pgrep/pkill' --argument-names cmd
|
||||
complete -c $cmd -xa '(__fish_complete_proc)'
|
||||
complete -c $cmd -s f -d 'Match pattern against full command line'
|
||||
complete -c $cmd -s g -d 'Only match processes in the process group' -xa '(__fish_complete_list , __fish_complete_groups)'
|
||||
complete -c $cmd -s G -d "Only match processes whose real group ID is listed. Group 0 is translated into $cmd\'s own process group" -xa '(__fish_complete_list , __fish_complete_groups)'
|
||||
complete -c $cmd -s g -d 'Only match processes in the process group' -xa "(__fish_stripprefix='^-\w*g' __fish_complete_list , __fish_complete_groups)"
|
||||
complete -c $cmd -s G -d "Only match processes whose real group ID is listed. Group 0 is translated into $cmd\'s own process group" -xa "(__fish_stripprefix='^-\w*G' __fish_complete_list , __fish_complete_groups)"
|
||||
complete -c $cmd -s n -d 'Select only the newest process'
|
||||
complete -c $cmd -s o -d 'Select only the oldest process'
|
||||
complete -c $cmd -s P -d 'Only match processes whose parent process ID is listed' -xa '(__fish_complete_list , __fish_complete_pids)'
|
||||
complete -c $cmd -s P -d 'Only match processes whose parent process ID is listed' -xa "(__fish_stripprefix='^-\w*P' __fish_complete_list , __fish_complete_pids)"
|
||||
complete -c $cmd -s s -d "Only match processes whose process session ID is listed. Session ID 0 is translated into $cmd\'s own session ID."
|
||||
complete -c $cmd -s t -d 'Only match processes whose controlling terminal is listed. The terminal name should be specified without the "/dev/" prefix' -r
|
||||
complete -c $cmd -s u -d 'Only match processes whose effective user ID is listed' -xa '(__fish_complete_list , __fish_complete_users)'
|
||||
complete -c $cmd -s U -d 'Only match processes whose real user ID is listed' -xa '(__fish_complete_list , __fish_complete_users)'
|
||||
complete -c $cmd -s u -d 'Only match processes whose effective user ID is listed' -xa "(__fish_stripprefix='^-\w*u' __fish_complete_list , __fish_complete_users)"
|
||||
complete -c $cmd -s U -d 'Only match processes whose real user ID is listed' -xa "(__fish_stripprefix='^-\w*U' __fish_complete_list , __fish_complete_users)"
|
||||
complete -c $cmd -s v -d 'Negates the matching'
|
||||
complete -c $cmd -s x -d ' Only match processes whose name (or command line if -f is specified) exactly match the pattern'
|
||||
end
|
||||
|
||||
@@ -3,7 +3,7 @@ function __fish_complete_ssh -d "common completions for ssh commands" --argument
|
||||
complete -c $command -s 6 -d "IPv6 only"
|
||||
complete -c $command -s A -d "Enables forwarding of the authentication agent"
|
||||
complete -c $command -s C -d "Compress all data"
|
||||
complete -c $command -s c -d "Encryption algorithm" -xa "(__fish_complete_list , __fish_ssh_ciphers)"
|
||||
complete -c $command -s c -d "Encryption algorithm" -xa "(__fish_stripprefix='^-\w*c' __fish_complete_list , __fish_ssh_ciphers)"
|
||||
complete -c $command -s F -d "Configuration file" -rF
|
||||
complete -c $command -s i -d "Identity key file" -rF
|
||||
complete -c $command -s J -d 'ProxyJump host' -xa "(__fish_complete_user_at_hosts)"
|
||||
|
||||
@@ -219,12 +219,17 @@ end" >$__fish_config_dir/config.fish
|
||||
end
|
||||
|
||||
# Notify terminals when $PWD changes via OSC 7 (issue #906).
|
||||
function __fish_update_cwd_osc --on-variable PWD --description 'Notify terminals when $PWD changes'
|
||||
set -l host $hostname
|
||||
if set -q KONSOLE_VERSION
|
||||
set host ''
|
||||
if not functions --query __fish_update_cwd_osc
|
||||
function __fish_update_cwd_osc --on-variable PWD --description 'Notify terminals when $PWD changes'
|
||||
set -l host $hostname
|
||||
if set -q KONSOLE_VERSION
|
||||
set host ''
|
||||
end
|
||||
if [ "$TERM" = dumb ]
|
||||
return
|
||||
end
|
||||
printf \e\]7\;file://%s%s\a $host (string escape --style=url -- $PWD)
|
||||
end
|
||||
printf \e\]7\;file://%s%s\a $host (string escape --style=url -- $PWD)
|
||||
end
|
||||
__fish_update_cwd_osc # Run once because we might have already inherited a PWD from an old tab
|
||||
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
|
||||
function __fish_seen_subcommand_from
|
||||
set -l regex (string escape --style=regex -- (commandline -pxc)[2..] | string join '|')
|
||||
string match -rq -- "^$regex"'$' $argv
|
||||
string match -rq -- "^($regex)\$" $argv
|
||||
end
|
||||
|
||||
@@ -59,6 +59,9 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod
|
||||
$legacy_bind --preset $argv \e\[1\;9C nextd-or-forward-word # iTerm2 < 3.5.12
|
||||
$legacy_bind --preset $argv \e\[1\;9D prevd-or-backward-word # iTerm2 < 3.5.12
|
||||
|
||||
bind --preset $argv alt-b prevd-or-backward-word
|
||||
bind --preset $argv alt-f nextd-or-forward-word
|
||||
|
||||
bind --preset $argv alt-up history-token-search-backward
|
||||
bind --preset $argv alt-down history-token-search-forward
|
||||
$legacy_bind --preset $argv \e\[1\;9A history-token-search-backward # iTerm2 < 3.5.12
|
||||
@@ -120,7 +123,7 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod
|
||||
bind --preset $argv alt-enter "commandline -i \n" expand-abbr
|
||||
bind --preset $argv ")" self-insert expand-abbr # Closing a command substitution.
|
||||
bind --preset $argv ctrl-space 'test -n "$(commandline)" && commandline -i " "'
|
||||
bind --preset $argv -k nul 'test -n "$(commandline)" && commandline -i " "'
|
||||
$legacy_bind --preset $argv -k nul 'test -n "$(commandline)" && commandline -i " "'
|
||||
# Shift-space behaves like space because it's easy to mistype.
|
||||
bind --preset $argv shift-space 'commandline -i " "' expand-abbr
|
||||
|
||||
|
||||
@@ -51,17 +51,17 @@ function fish_default_key_bindings -d "emacs-like key binds"
|
||||
bind --preset $argv ctrl-/ undo
|
||||
bind --preset $argv ctrl-_ undo # XTerm idiosyncracy, can get rid of this once we go full CSI u
|
||||
bind --preset $argv ctrl-z undo
|
||||
bind --preset $argv ctrl-Z redo
|
||||
bind --preset $argv ctrl-shift-z redo
|
||||
bind --preset $argv alt-/ redo
|
||||
bind --preset $argv alt-t transpose-words
|
||||
bind --preset $argv alt-u upcase-word
|
||||
|
||||
bind --preset $argv alt-c capitalize-word
|
||||
bind --preset $argv alt-backspace backward-kill-word
|
||||
bind --preset $argv alt-delete kill-word
|
||||
bind --preset $argv ctrl-alt-h backward-kill-word
|
||||
bind --preset $argv ctrl-backspace backward-kill-word
|
||||
bind --preset $argv ctrl-delete kill-word
|
||||
bind --preset $argv alt-b prevd-or-backward-word
|
||||
bind --preset $argv alt-f nextd-or-forward-word
|
||||
|
||||
bind --preset $argv alt-\< beginning-of-buffer
|
||||
bind --preset $argv alt-\> end-of-buffer
|
||||
|
||||
@@ -7,16 +7,16 @@ function fish_print_hg_root
|
||||
# Find an hg directory above $PWD
|
||||
# without calling `hg root` because that's too slow
|
||||
set -l root
|
||||
set -l dir (pwd -P 2>/dev/null)
|
||||
set -l dir "$(pwd -P 2>/dev/null)"
|
||||
or return 1
|
||||
|
||||
while test $dir != /
|
||||
while not contains -- "$dir" "" / .
|
||||
if test -f $dir'/.hg/dirstate'
|
||||
echo $dir/.hg
|
||||
return 0
|
||||
end
|
||||
# Go up one directory
|
||||
set dir (string replace -r '[^/]*/?$' '' $dir)
|
||||
set dir (path dirname -- $dir)
|
||||
end
|
||||
|
||||
return 1
|
||||
|
||||
@@ -109,18 +109,24 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
|
||||
bind -s --preset -M insert ctrl-n accept-autosuggestion
|
||||
|
||||
# Vi/Vim doesn't support these keys in insert mode but that seems silly so we do so anyway.
|
||||
bind -s --preset -M insert -k home beginning-of-line
|
||||
bind -s --preset -M default -k home beginning-of-line
|
||||
bind -s --preset -M insert -k end end-of-line
|
||||
bind -s --preset -M default -k end end-of-line
|
||||
bind -s --preset -M insert home beginning-of-line
|
||||
$legacy_bind -s --preset -M insert -k home beginning-of-line
|
||||
bind -s --preset -M default home beginning-of-line
|
||||
$legacy_bind -s --preset -M default -k home beginning-of-line
|
||||
bind -s --preset -M insert end end-of-line
|
||||
$legacy_bind -s --preset -M insert -k end end-of-line
|
||||
bind -s --preset -M default end end-of-line
|
||||
$legacy_bind -s --preset -M default -k end end-of-line
|
||||
|
||||
# Vi moves the cursor back if, after deleting, it is at EOL.
|
||||
# To emulate that, move forward, then backward, which will be a NOP
|
||||
# if there is something to move forward to.
|
||||
bind -s --preset -M default x delete-char 'set fish_cursor_end_mode exclusive' forward-single-char backward-char 'set fish_cursor_end_mode inclusive'
|
||||
bind -s --preset -M default X backward-delete-char
|
||||
bind -s --preset -M insert -k dc delete-char forward-single-char backward-char
|
||||
bind -s --preset -M default -k dc delete-char 'set fish_cursor_end_mode exclusive' forward-single-char backward-char 'set fish_cursor_end_mode inclusive'
|
||||
bind -s --preset -M insert delete delete-char forward-single-char backward-char
|
||||
$legacy_bind -s --preset -M insert -k dc delete-char forward-single-char backward-char
|
||||
bind -s --preset -M default delete delete-char 'set fish_cursor_end_mode exclusive' forward-single-char backward-char 'set fish_cursor_end_mode inclusive'
|
||||
$legacy_bind -s --preset -M default -k dc delete-char 'set fish_cursor_end_mode exclusive' forward-single-char backward-char 'set fish_cursor_end_mode inclusive'
|
||||
|
||||
# Backspace deletes a char in insert mode, but not in normal/default mode.
|
||||
bind -s --preset -M insert backspace backward-delete-char
|
||||
@@ -241,7 +247,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
|
||||
# in vim p means paste *after* current character, so go forward a char before pasting
|
||||
# also in vim, P means paste *at* current position (like at '|' with cursor = line),
|
||||
# \ so there's no need to go back a char, just paste it without moving
|
||||
bind -s --preset p 'set -g fish_cursor_end_mode exclusive' forward-char 'set -g fish_cursor_end_modefish_cursor_end_modeinclusive' yank
|
||||
bind -s --preset p 'set -g fish_cursor_end_mode exclusive' forward-char 'set -g fish_cursor_end_mode inclusive' yank
|
||||
bind -s --preset P yank
|
||||
bind -s --preset g,p yank-pop
|
||||
|
||||
@@ -255,10 +261,10 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
|
||||
# Lowercase r, enters replace_one mode
|
||||
#
|
||||
bind -s --preset -m replace_one r repaint-mode
|
||||
bind -s --preset -M replace_one -m default '' delete-char self-insert backward-char repaint-mode
|
||||
bind -s --preset -M replace_one -m default enter 'commandline -f delete-char; commandline -i \n; commandline -f backward-char; commandline -f repaint-mode'
|
||||
bind -s --preset -M replace_one -m default ctrl-j 'commandline -f delete-char; commandline -i \n; commandline -f backward-char; commandline -f repaint-mode'
|
||||
bind -s --preset -M replace_one -m default ctrl-m 'commandline -f delete-char; commandline -i \n; commandline -f backward-char; commandline -f repaint-mode'
|
||||
bind -s --preset -M replace_one -m default '' 'set -g fish_cursor_end_mode exclusive' delete-char self-insert backward-char repaint-mode 'set -g fish_cursor_end_mode inclusive'
|
||||
bind -s --preset -M replace_one -m default enter 'set -g fish_cursor_end_mode exclusive' 'commandline -f delete-char; commandline -i \n; commandline -f backward-char' repaint-mode 'set -g fish_cursor_end_mode inclusive'
|
||||
bind -s --preset -M replace_one -m default ctrl-j 'set -g fish_cursor_end_mode exclusive' 'commandline -f delete-char; commandline -i \n; commandline -f backward-char' repaint-mode 'set -g fish_cursor_end_mode inclusive'
|
||||
bind -s --preset -M replace_one -m default ctrl-m 'set -g fish_cursor_end_mode exclusive' 'commandline -f delete-char; commandline -i \n; commandline -f backward-char' repaint-mode 'set -g fish_cursor_end_mode inclusive'
|
||||
bind -s --preset -M replace_one -m default escape cancel repaint-mode
|
||||
bind -s --preset -M replace_one -m default ctrl-\[ cancel repaint-mode
|
||||
|
||||
|
||||
@@ -656,7 +656,8 @@ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
impl Acceptor for $name {
|
||||
#[allow(unused_variables)]
|
||||
fn accept<'a>(&'a self, visitor: &mut dyn NodeVisitor<'a>, reversed: bool) {
|
||||
accept_list_visitor!(Self, accept, visit, self, visitor, reversed, $contents);
|
||||
let _ =
|
||||
accept_list_visitor!(Self, accept, visit, self, visitor, reversed, $contents);
|
||||
}
|
||||
}
|
||||
impl AcceptorMut for $name {
|
||||
@@ -743,7 +744,7 @@ macro_rules! implement_acceptor_for_branch {
|
||||
impl Acceptor for $name {
|
||||
#[allow(unused_variables)]
|
||||
fn accept<'a>(&'a self, visitor: &mut dyn NodeVisitor<'a>, reversed: bool){
|
||||
visitor_accept_field!(
|
||||
let _ = visitor_accept_field!(
|
||||
Self,
|
||||
accept,
|
||||
visit,
|
||||
@@ -3718,7 +3719,7 @@ fn allocate<T: NodeMut + Default>(&self) -> Box<T> {
|
||||
// Return the resulting Node pointer. It is never null.
|
||||
fn allocate_visit<T: NodeMut + Default>(&mut self) -> Box<T> {
|
||||
let mut result = Box::<T>::default();
|
||||
self.visit_mut(&mut *result);
|
||||
let _ = self.visit_mut(&mut *result);
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
175
src/bin/fish.rs
175
src/bin/fish.rs
@@ -21,6 +21,8 @@
|
||||
#![allow(unstable_name_collisions)]
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
use fish::common::{get_executable_path, wcs2osstring};
|
||||
#[allow(unused_imports)]
|
||||
use fish::future::IsSomeAnd;
|
||||
use fish::{
|
||||
@@ -29,12 +31,12 @@
|
||||
BUILTIN_ERR_MISSING, BUILTIN_ERR_UNKNOWN, STATUS_CMD_OK, STATUS_CMD_UNKNOWN,
|
||||
},
|
||||
common::{
|
||||
escape, get_executable_path, save_term_foreground_process_group, scoped_push_replacer,
|
||||
str2wcstring, wcs2osstring, wcs2string, PACKAGE_NAME, PROFILING_ACTIVE, PROGRAM_NAME,
|
||||
escape, save_term_foreground_process_group, scoped_push_replacer, str2wcstring, wcs2string,
|
||||
PACKAGE_NAME, PROFILING_ACTIVE, PROGRAM_NAME,
|
||||
},
|
||||
env::{
|
||||
environment::{env_init, EnvStack, Environment},
|
||||
ConfigPaths, EnvMode, Statuses,
|
||||
ConfigPaths, EnvMode, Statuses, CONFIG_PATHS,
|
||||
},
|
||||
eprintf,
|
||||
event::{self, Event},
|
||||
@@ -65,22 +67,16 @@
|
||||
use std::fs::File;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::{env, ops::ControlFlow};
|
||||
|
||||
const DOC_DIR: &str = env!("DOCDIR");
|
||||
const DATA_DIR: &str = env!("DATADIR");
|
||||
const DATA_DIR_SUBDIR: &str = env!("DATADIR_SUBDIR");
|
||||
const SYSCONF_DIR: &str = env!("SYSCONFDIR");
|
||||
const BIN_DIR: &str = env!("BINDIR");
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
// Disable for clippy because otherwise it would require sphinx
|
||||
#[cfg(not(clippy))]
|
||||
fn install(confirm: bool, dir: PathBuf) -> bool {
|
||||
fn install(confirm: bool, dir: &PathBuf) -> bool {
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
@@ -192,7 +188,8 @@ fn install(confirm: bool, dir: PathBuf) -> bool {
|
||||
}
|
||||
|
||||
#[cfg(any(clippy, not(feature = "installable")))]
|
||||
fn install(_confirm: bool, _dir: PathBuf) -> bool {
|
||||
#[allow(dead_code)]
|
||||
fn install(_confirm: bool, _dir: &PathBuf) -> bool {
|
||||
eprintln!("Fish was built without support for self-installation");
|
||||
return false;
|
||||
}
|
||||
@@ -264,128 +261,6 @@ fn print_rusage_self() {
|
||||
eprintln!(" signals: {signals}");
|
||||
}
|
||||
|
||||
fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
// PORTING: why is this not just an associated method on ConfigPaths?
|
||||
|
||||
let mut paths = ConfigPaths::default();
|
||||
let mut done = false;
|
||||
let exec_path = get_executable_path(argv0.as_ref());
|
||||
if let Ok(exec_path) = exec_path.canonicalize() {
|
||||
FLOG!(
|
||||
config,
|
||||
format!("exec_path: {:?}, argv[0]: {:?}", exec_path, argv0.as_ref())
|
||||
);
|
||||
// TODO: we should determine program_name from argv0 somewhere in this file
|
||||
|
||||
// Detect if we're running right out of the CMAKE build directory
|
||||
if exec_path.starts_with(env!("CARGO_MANIFEST_DIR")) {
|
||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
FLOG!(
|
||||
config,
|
||||
"Running out of target directory, using paths relative to CARGO_MANIFEST_DIR:\n",
|
||||
manifest_dir.display()
|
||||
);
|
||||
done = true;
|
||||
paths = ConfigPaths {
|
||||
data: manifest_dir.join("share"),
|
||||
sysconf: manifest_dir.join("etc"),
|
||||
doc: manifest_dir.join("user_doc/html"),
|
||||
bin: Some(exec_path.parent().unwrap().to_owned()),
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// The next check is that we are in a relocatable directory tree
|
||||
if exec_path.ends_with("bin/fish") {
|
||||
let base_path = exec_path.parent().unwrap().parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
// One obvious path is ~/.local (with fish in ~/.local/bin/).
|
||||
// If we picked ~/.local/share/fish as our data path,
|
||||
// we would install there and erase history.
|
||||
// So let's isolate us a bit more.
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/fish/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share/fish"),
|
||||
sysconf: base_path.join("etc/fish"),
|
||||
doc: base_path.join("share/doc/fish"),
|
||||
bin: Some(base_path.join("bin")),
|
||||
}
|
||||
} else if exec_path.ends_with("fish") {
|
||||
FLOG!(
|
||||
config,
|
||||
"'fish' not in a 'bin/', trying paths relative to source tree"
|
||||
);
|
||||
let base_path = exec_path.parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share"),
|
||||
sysconf: base_path.join("etc"),
|
||||
doc: base_path.join("user_doc/html"),
|
||||
bin: Some(base_path.to_path_buf()),
|
||||
}
|
||||
}
|
||||
|
||||
if paths.data.exists() && paths.sysconf.exists() {
|
||||
// The docs dir may not exist; in that case fall back to the compiled in path.
|
||||
if !paths.doc.exists() {
|
||||
paths.doc = PathBuf::from(DOC_DIR);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// Fall back to what got compiled in.
|
||||
let data = if cfg!(feature = "installable") {
|
||||
let Some(home) = fish::env::get_home() else {
|
||||
FLOG!(
|
||||
error,
|
||||
"Cannot find home directory and will refuse to read configuration.\n",
|
||||
"Consider installing into a directory tree with `fish --install=PATH`."
|
||||
);
|
||||
return paths;
|
||||
};
|
||||
|
||||
PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
} else {
|
||||
PathBuf::from(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
};
|
||||
let bin = if cfg!(feature = "installable") {
|
||||
exec_path.parent().map(|x| x.to_path_buf())
|
||||
} else {
|
||||
Some(PathBuf::from(BIN_DIR))
|
||||
};
|
||||
|
||||
FLOG!(config, "Using compiled in paths:");
|
||||
paths = ConfigPaths {
|
||||
data,
|
||||
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
|
||||
doc: DOC_DIR.into(),
|
||||
bin,
|
||||
}
|
||||
}
|
||||
|
||||
FLOGF!(
|
||||
config,
|
||||
"determine_config_directory_paths() results:\npaths.data: %ls\npaths.sysconf: \
|
||||
%ls\npaths.doc: %ls\npaths.bin: %ls",
|
||||
paths.data.display().to_string(),
|
||||
paths.sysconf.display().to_string(),
|
||||
paths.doc.display().to_string(),
|
||||
paths
|
||||
.bin
|
||||
.clone()
|
||||
.map(|x| x.display().to_string())
|
||||
.unwrap_or("|not found|".to_string()),
|
||||
);
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
// Source the file config.fish in the given directory.
|
||||
// Returns true if successful, false if not.
|
||||
fn source_config_in_directory(parser: &Parser, dir: &wstr) -> bool {
|
||||
@@ -463,7 +338,7 @@ fn read_init(parser: &Parser, paths: &ConfigPaths) {
|
||||
);
|
||||
}
|
||||
|
||||
install(true, PathBuf::from(wcs2osstring(&datapath)));
|
||||
install(true, &PathBuf::from(wcs2osstring(&datapath)));
|
||||
// We try to go on if installation failed (or was rejected) here
|
||||
// If the assets are missing, we will trigger a later error,
|
||||
// if they are outdated, things will probably (tm) work somewhat.
|
||||
@@ -592,8 +467,9 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
||||
// path/share/fish/ is the data directory
|
||||
// path/etc/fish is sysconf????
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
let dir = PathBuf::from(wcs2osstring(path));
|
||||
if install(true, dir.join("share/fish/install")) {
|
||||
if install(true, &dir.join("share/fish/install")) {
|
||||
for sub in &["share/fish/install", "etc/fish", "bin"] {
|
||||
let p = dir.join(sub);
|
||||
let Ok(_) = fs::create_dir_all(p.clone()) else {
|
||||
@@ -626,14 +502,12 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let paths = Some(determine_config_directory_paths(OsString::from_vec(
|
||||
wcs2string(&args[0]),
|
||||
)));
|
||||
let paths = Some(&*CONFIG_PATHS);
|
||||
let Some(paths) = paths else {
|
||||
FLOG!(error, "Cannot find config paths");
|
||||
std::process::exit(1);
|
||||
};
|
||||
install(true, paths.data);
|
||||
install(true, &paths.data);
|
||||
}
|
||||
}
|
||||
'l' => opts.is_login = true,
|
||||
@@ -817,18 +691,19 @@ fn throwing_main() -> i32 {
|
||||
save_term_foreground_process_group();
|
||||
}
|
||||
|
||||
let mut paths: Option<ConfigPaths> = None;
|
||||
let mut paths: Option<&ConfigPaths> = None;
|
||||
// If we're not executing, there's no need to find the config.
|
||||
if !opts.no_exec {
|
||||
paths = Some(determine_config_directory_paths(OsString::from_vec(
|
||||
wcs2string(&args[0]),
|
||||
)));
|
||||
paths = Some(&*CONFIG_PATHS);
|
||||
env_init(
|
||||
paths.as_ref(),
|
||||
paths,
|
||||
/* do uvars */ !opts.no_config,
|
||||
/* default paths */ opts.no_config,
|
||||
);
|
||||
}
|
||||
paths
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Set features early in case other initialization depends on them.
|
||||
// Start with the ones set in the environment, then those set on the command line (so the
|
||||
@@ -845,7 +720,7 @@ fn throwing_main() -> i32 {
|
||||
|
||||
// Construct the root parser!
|
||||
let env = Rc::new(EnvStack::globals().create_child(true /* dispatches_var_changes */));
|
||||
let parser: &Parser = &Parser::new(env, CancelBehavior::Clear);
|
||||
let parser = &Parser::new(env, CancelBehavior::Clear);
|
||||
parser.set_syncs_uvars(!opts.no_config);
|
||||
|
||||
if !opts.no_exec && !opts.no_config {
|
||||
@@ -1008,7 +883,6 @@ fn fish_xdm_login_hack_hack_hack_hack(cmds: &mut [OsString], args: &[WString]) -
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut result = false;
|
||||
let cmd = &cmds[0];
|
||||
if cmd == "exec \"${@}\"" || cmd == "exec \"$@\"" {
|
||||
// We're going to construct a new command that starts with exec, and then has the
|
||||
@@ -1020,7 +894,8 @@ fn fish_xdm_login_hack_hack_hack_hack(cmds: &mut [OsString], args: &[WString]) -
|
||||
}
|
||||
|
||||
cmds[0] = new_cmd;
|
||||
result = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
eprintf, fprintf,
|
||||
input::input_terminfo_get_name,
|
||||
input_common::{
|
||||
terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent, InputEventQueue,
|
||||
InputEventQueuer,
|
||||
match_key_event_to_key, terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent,
|
||||
InputEventQueue, InputEventQueuer, KeyEvent,
|
||||
},
|
||||
key::{self, char_to_symbol, Key},
|
||||
key::{char_to_symbol, Key},
|
||||
panic::panic_handler,
|
||||
print_help::print_help,
|
||||
printf,
|
||||
@@ -37,15 +37,21 @@
|
||||
};
|
||||
|
||||
/// Return true if the recent sequence of characters indicates the user wants to exit the program.
|
||||
fn should_exit(recent_keys: &mut Vec<Key>, key: Key) -> bool {
|
||||
recent_keys.push(key);
|
||||
fn should_exit(recent_keys: &mut Vec<KeyEvent>, key_evt: KeyEvent) -> bool {
|
||||
recent_keys.push(key_evt);
|
||||
|
||||
for evt in [VINTR, VEOF] {
|
||||
let modes = shell_modes();
|
||||
let cc = Key::from_single_byte(modes.c_cc[evt]);
|
||||
|
||||
if key == cc {
|
||||
if recent_keys.iter().rev().nth(1) == Some(&cc) {
|
||||
if match_key_event_to_key(&key_evt, &cc).is_some() {
|
||||
if recent_keys
|
||||
.iter()
|
||||
.rev()
|
||||
.nth(1)
|
||||
.and_then(|&prev| match_key_event_to_key(&prev, &cc))
|
||||
.is_some()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
eprintf!(
|
||||
@@ -101,9 +107,6 @@ fn process_input(continuous_mode: bool, verbose: bool) -> i32 {
|
||||
continue;
|
||||
};
|
||||
let c = kevt.key.codepoint;
|
||||
if c == key::Invalid {
|
||||
continue;
|
||||
}
|
||||
if verbose {
|
||||
printf!("# decoded from: ");
|
||||
for byte in kevt.seq.chars() {
|
||||
@@ -111,7 +114,27 @@ fn process_input(continuous_mode: bool, verbose: bool) -> i32 {
|
||||
}
|
||||
printf!("\n");
|
||||
}
|
||||
printf!("bind %s 'do something'\n", kevt.key);
|
||||
let have_shifted_key = kevt.key.shifted_codepoint != '\0';
|
||||
let mut keys = vec![(kevt.key.key, "")];
|
||||
if have_shifted_key {
|
||||
let mut shifted_key = kevt.key.key;
|
||||
shifted_key.modifiers.shift = false;
|
||||
shifted_key.codepoint = kevt.key.shifted_codepoint;
|
||||
keys.push((shifted_key, "shifted key"));
|
||||
}
|
||||
if kevt.key.base_layout_codepoint != '\0' {
|
||||
let mut base_layout_key = kevt.key.key;
|
||||
base_layout_key.codepoint = kevt.key.base_layout_codepoint;
|
||||
keys.push((base_layout_key, "physical key"));
|
||||
}
|
||||
for (key, explanation) in keys {
|
||||
printf!(
|
||||
"bind %s 'do something'%s%s\n",
|
||||
key,
|
||||
if explanation.is_empty() { "" } else { " # " },
|
||||
explanation,
|
||||
);
|
||||
}
|
||||
if let Some(name) = sequence_name(&mut recent_chars1, c) {
|
||||
printf!("bind -k %ls 'do something'\n", name);
|
||||
}
|
||||
|
||||
@@ -17,12 +17,13 @@ fn send_to_bg(
|
||||
let err = {
|
||||
let job = &jobs[job_pos];
|
||||
wgettext_fmt!(
|
||||
"%ls: Can't put job %s, '%ls' to background because it is not under job control\n",
|
||||
cmd,
|
||||
job.job_id().to_wstring(),
|
||||
job.command()
|
||||
)
|
||||
"%ls: Can't put job %s, '%ls' to background because it is not under job control\n",
|
||||
cmd,
|
||||
job.job_id().to_wstring(),
|
||||
job.command()
|
||||
)
|
||||
};
|
||||
drop(jobs);
|
||||
builtin_print_help_error(parser, streams, cmd, &err);
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ enum TokenMode {
|
||||
/// \param buff the original command line buffer
|
||||
/// \param cursor_pos the position of the cursor in the command line
|
||||
fn replace_part(
|
||||
parser: &Parser,
|
||||
range: Range<usize>,
|
||||
insert: &wstr,
|
||||
insert_mode: AppendMode,
|
||||
@@ -86,9 +87,9 @@ fn replace_part(
|
||||
|
||||
out.push_utfstr(&buff[range.end..]);
|
||||
if search_field_mode {
|
||||
commandline_set_search_field(out, Some(out_pos));
|
||||
commandline_set_search_field(parser, out, Some(out_pos));
|
||||
} else {
|
||||
commandline_set_buffer(Some(out), Some(out_pos));
|
||||
commandline_set_buffer(parser, Some(out), Some(out_pos));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,7 +466,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
||||
}
|
||||
line_offset + new_coord
|
||||
};
|
||||
commandline_set_buffer(None, Some(new_pos));
|
||||
commandline_set_buffer(parser, None, Some(new_pos));
|
||||
} else {
|
||||
streams.out.append(sprintf!(
|
||||
"%d\n",
|
||||
@@ -631,7 +632,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
||||
.saturating_add_signed(isize::try_from(new_pos).unwrap()),
|
||||
current_buffer.len(),
|
||||
);
|
||||
commandline_set_buffer(None, Some(new_pos));
|
||||
commandline_set_buffer(parser, None, Some(new_pos));
|
||||
} else {
|
||||
streams
|
||||
.out
|
||||
@@ -652,6 +653,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
||||
);
|
||||
} else if positional_args == 1 {
|
||||
replace_part(
|
||||
parser,
|
||||
range,
|
||||
args[w.wopt_index],
|
||||
append_mode,
|
||||
@@ -662,6 +664,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
||||
} else {
|
||||
let sb = join_strings(&w.argv[w.wopt_index..], '\n');
|
||||
replace_part(
|
||||
parser,
|
||||
range,
|
||||
&sb,
|
||||
append_mode,
|
||||
|
||||
@@ -176,7 +176,7 @@ fn builtin_complete_remove_cmd(
|
||||
|
||||
if !removed {
|
||||
// This means that all loops were empty.
|
||||
complete_remove_all(cmd.to_owned(), cmd_is_path);
|
||||
complete_remove_all(cmd.to_owned(), cmd_is_path, /*explicit=*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,7 +517,8 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
||||
next.flags,
|
||||
faux_cmdline,
|
||||
&mut tmp_cursor,
|
||||
false,
|
||||
/*append_only=*/ false,
|
||||
/*is_unique=*/ false,
|
||||
);
|
||||
|
||||
// completion_apply_to_command_line will append a space unless COMPLETE_NO_SPACE
|
||||
|
||||
@@ -148,7 +148,7 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Optio
|
||||
let job_group = job.group();
|
||||
job_group.set_is_foreground(true);
|
||||
if job.entitled_to_terminal() {
|
||||
crate::input_common::terminal_protocols_disable_ifn();
|
||||
crate::input_common::terminal_protocols_disable_ifn(false);
|
||||
}
|
||||
let tmodes = job_group.tmodes.borrow();
|
||||
if job_group.wants_terminal() && tmodes.is_some() {
|
||||
|
||||
@@ -92,6 +92,14 @@ fn parse_cmd_opts(
|
||||
// A positional argument we got because we use RETURN_IN_ORDER.
|
||||
let woptarg = w.woptarg.unwrap().to_owned();
|
||||
if handling_named_arguments {
|
||||
if is_read_only(&woptarg) {
|
||||
streams.err.append(wgettext_fmt!(
|
||||
"%ls: variable '%ls' is read-only\n",
|
||||
cmd,
|
||||
woptarg
|
||||
));
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
opts.named_arguments.push(woptarg);
|
||||
} else {
|
||||
streams.err.append(wgettext_fmt!(
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
use crate::env::Environment;
|
||||
use crate::env::READ_BYTE_LIMIT;
|
||||
use crate::env::{EnvVar, EnvVarFlags};
|
||||
use crate::input_common::decode_input_byte;
|
||||
use crate::input_common::terminal_protocols_disable_ifn;
|
||||
use crate::libc::MB_CUR_MAX;
|
||||
use crate::input_common::DecodeState;
|
||||
use crate::nix::isatty;
|
||||
use crate::reader::commandline_set_buffer;
|
||||
use crate::reader::ReaderConfig;
|
||||
@@ -23,7 +24,6 @@
|
||||
use crate::wcstringutil::split_about;
|
||||
use crate::wcstringutil::split_string_tok;
|
||||
use crate::wutil;
|
||||
use crate::wutil::encoding::mbrtowc;
|
||||
use crate::wutil::encoding::zero_mbstate;
|
||||
use crate::wutil::perror;
|
||||
use libc::SEEK_CUR;
|
||||
@@ -234,7 +234,11 @@ fn read_interactive(
|
||||
|
||||
// Keep in-memory history only.
|
||||
reader_push(parser, L!(""), conf);
|
||||
commandline_set_buffer(Some(commandline.to_owned()), None);
|
||||
let _modifiable_commandline = scoped_push_replacer(
|
||||
|new_value| std::mem::replace(&mut parser.libdata_mut().readonly_commandline, new_value),
|
||||
false,
|
||||
);
|
||||
commandline_set_buffer(parser, Some(commandline.to_owned()), None);
|
||||
|
||||
let mline = {
|
||||
let _interactive = scoped_push_replacer(
|
||||
@@ -244,7 +248,7 @@ fn read_interactive(
|
||||
|
||||
reader_readline(parser, nchars)
|
||||
};
|
||||
terminal_protocols_disable_ifn();
|
||||
terminal_protocols_disable_ifn(false);
|
||||
if let Some(line) = mline {
|
||||
*buff = line;
|
||||
if nchars > 0 && nchars < buff.len() {
|
||||
@@ -337,70 +341,61 @@ fn read_one_char_at_a_time(
|
||||
split_null: bool,
|
||||
) -> Option<c_int> {
|
||||
let mut exit_res = STATUS_CMD_OK;
|
||||
let mut eof = false;
|
||||
let mut nbytes = 0;
|
||||
|
||||
let mut unconsumed = vec![];
|
||||
|
||||
loop {
|
||||
let mut finished = false;
|
||||
let mut res = '\x00';
|
||||
let mut state = zero_mbstate();
|
||||
|
||||
while !finished {
|
||||
let chars_read = buff.len();
|
||||
let res = loop {
|
||||
let mut b = [0_u8; 1];
|
||||
match read_blocked(fd, &mut b) {
|
||||
Ok(0) | Err(_) => {
|
||||
eof = true;
|
||||
break;
|
||||
break None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let b = b[0];
|
||||
|
||||
unconsumed.push(b);
|
||||
nbytes += 1;
|
||||
if MB_CUR_MAX() == 1 {
|
||||
res = char::from(b);
|
||||
finished = true;
|
||||
} else {
|
||||
let sz = unsafe {
|
||||
mbrtowc(
|
||||
std::ptr::addr_of_mut!(res).cast(),
|
||||
std::ptr::addr_of!(b).cast(),
|
||||
1,
|
||||
&mut state,
|
||||
)
|
||||
} as isize;
|
||||
if sz == -1 {
|
||||
let mut consumed = 0;
|
||||
match decode_input_byte(buff, &mut state, &unconsumed, &mut consumed) {
|
||||
DecodeState::Incomplete => continue,
|
||||
DecodeState::Complete => {
|
||||
unconsumed.clear();
|
||||
break Some(buff.as_char_slice().last().unwrap());
|
||||
}
|
||||
DecodeState::Error => {
|
||||
state = zero_mbstate();
|
||||
} else if sz != -2 {
|
||||
finished = true;
|
||||
unconsumed.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if nbytes > READ_BYTE_LIMIT.load(Ordering::Relaxed) {
|
||||
// Historical behavior: do not include the codepoint that made us overflow.
|
||||
buff.truncate(chars_read);
|
||||
exit_res = STATUS_READ_TOO_MUCH;
|
||||
break;
|
||||
}
|
||||
if eof {
|
||||
let Some(&res) = res else {
|
||||
// EOF
|
||||
if buff.is_empty() {
|
||||
exit_res = STATUS_CMD_ERROR;
|
||||
}
|
||||
break;
|
||||
};
|
||||
if res == if split_null { '\0' } else { '\n' } {
|
||||
buff.pop();
|
||||
break;
|
||||
}
|
||||
if !split_null && res == '\n' {
|
||||
break;
|
||||
}
|
||||
if split_null && res == '\0' {
|
||||
break;
|
||||
}
|
||||
|
||||
buff.push(res);
|
||||
if nchars > 0 && nchars <= buff.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if buff.is_empty() && eof {
|
||||
exit_res = STATUS_CMD_ERROR;
|
||||
}
|
||||
|
||||
exit_res
|
||||
}
|
||||
|
||||
|
||||
@@ -582,11 +582,10 @@ pub fn status(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O
|
||||
STATUS_CURRENT_CMD => {
|
||||
let command = &parser.libdata().status_vars.command;
|
||||
if !command.is_empty() {
|
||||
streams.out.append(command);
|
||||
streams.out.appendln(command);
|
||||
} else {
|
||||
streams.out.appendln(*PROGRAM_NAME.get().unwrap());
|
||||
}
|
||||
streams.out.append_char('\n');
|
||||
}
|
||||
STATUS_CURRENT_COMMANDLINE => {
|
||||
let commandline = &parser.libdata().status_vars.commandline;
|
||||
|
||||
@@ -684,10 +684,11 @@ fn parse_4_arg_expression(
|
||||
|
||||
if let Token::UnaryBoolean(token) = first_token {
|
||||
let subject = self.parse_3_arg_expression(start + 1, end)?;
|
||||
let range = start..subject.range().end;
|
||||
UnaryOperator {
|
||||
subject,
|
||||
token,
|
||||
range: start..end,
|
||||
range,
|
||||
}
|
||||
.into_some_box()
|
||||
} else if first_token == Token::ParenOpen {
|
||||
|
||||
@@ -103,6 +103,12 @@ fn test_test() {
|
||||
assert!(run_test_test(1, &["!", "15", "-ge", "10"]));
|
||||
assert!(run_test_test(0, &["!", "!", "15", "-ge", "10"]));
|
||||
|
||||
assert!(run_test_test(0, &[
|
||||
"(", "-d", "/", ")",
|
||||
"-o",
|
||||
"(", "!", "-d", "/", ")",
|
||||
]));
|
||||
|
||||
assert!(run_test_test(0, &["0", "-ne", "1", "-a", "0", "-eq", "0"]));
|
||||
assert!(run_test_test(0, &["0", "-ne", "1", "-a", "-n", "5"]));
|
||||
assert!(run_test_test(0, &["-n", "5", "-a", "10", "-gt", "5"]));
|
||||
|
||||
@@ -1935,19 +1935,16 @@ fn complete_custom(&mut self, cmd: &wstr, cmdline: &wstr, ad: &mut CustomArgData
|
||||
// Perhaps set a transient commandline so that custom completions
|
||||
// builtin_commandline will refer to the wrapped command. But not if
|
||||
// we're doing autosuggestions.
|
||||
let mut _remove_transient = None;
|
||||
let wants_transient =
|
||||
(ad.wrap_depth > 0 || !ad.var_assignments.is_empty()) && !is_autosuggest;
|
||||
if wants_transient {
|
||||
let _remove_transient = (!is_autosuggest).then(|| {
|
||||
let parser = self.ctx.parser();
|
||||
parser
|
||||
.libdata_mut()
|
||||
.transient_commandlines
|
||||
.push(cmdline.to_owned());
|
||||
_remove_transient = Some(ScopeGuard::new((), move |_| {
|
||||
ScopeGuard::new((), move |_| {
|
||||
parser.libdata_mut().transient_commandlines.pop();
|
||||
}));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Maybe apply variable assignments.
|
||||
let _restore_vars = self.apply_var_assignments(ad.var_assignments);
|
||||
@@ -2342,14 +2339,14 @@ pub fn complete_remove(cmd: WString, cmd_is_path: bool, option: &wstr, typ: Comp
|
||||
}
|
||||
|
||||
/// Removes all completions for a given command.
|
||||
pub fn complete_remove_all(cmd: WString, cmd_is_path: bool) {
|
||||
pub fn complete_remove_all(cmd: WString, cmd_is_path: bool, explicit: bool) {
|
||||
let mut completion_map = COMPLETION_MAP.lock().expect("mutex poisoned");
|
||||
let idx = CompletionEntryIndex {
|
||||
name: cmd,
|
||||
is_path: cmd_is_path,
|
||||
};
|
||||
let removed = completion_map.remove(&idx).is_some();
|
||||
if !removed && !idx.is_path {
|
||||
if explicit && !removed && !idx.is_path {
|
||||
COMPLETION_TOMBSTONES.lock().unwrap().insert(idx.name);
|
||||
}
|
||||
}
|
||||
@@ -2522,7 +2519,7 @@ pub fn complete_invalidate_path() {
|
||||
.expect("mutex poisoned")
|
||||
.get_autoloaded_commands();
|
||||
for cmd in cmds {
|
||||
complete_remove_all(cmd, false /* not a path */);
|
||||
complete_remove_all(cmd, /*cmd_is_path=*/ false, /*explicit=*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
168
src/env/config_paths.rs
vendored
Normal file
168
src/env/config_paths.rs
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
use super::ConfigPaths;
|
||||
use crate::env;
|
||||
use crate::{common::get_executable_path, FLOG, FLOGF};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const DOC_DIR: &str = env!("DOCDIR");
|
||||
const DATA_DIR: &str = env!("DATADIR");
|
||||
const DATA_DIR_SUBDIR: &str = env!("DATADIR_SUBDIR");
|
||||
const SYSCONF_DIR: &str = env!("SYSCONFDIR");
|
||||
const BIN_DIR: &str = env!("BINDIR");
|
||||
const LOCALE_DIR: &str = env!("LOCALEDIR");
|
||||
|
||||
pub static CONFIG_PATHS: Lazy<ConfigPaths> = Lazy::new(|| {
|
||||
// Read the current executable and follow all symlinks to it.
|
||||
// OpenBSD has issues with `std::env::current_exe`, see gh-9086 and
|
||||
// https://github.com/rust-lang/rust/issues/60560
|
||||
let argv0 = PathBuf::from(std::env::args().next().unwrap());
|
||||
let argv0 = if argv0.exists() {
|
||||
argv0
|
||||
} else {
|
||||
std::env::current_exe().unwrap_or(argv0)
|
||||
};
|
||||
let argv0 = argv0.canonicalize().unwrap_or(argv0);
|
||||
determine_config_directory_paths(argv0)
|
||||
});
|
||||
|
||||
fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
// PORTING: why is this not just an associated method on ConfigPaths?
|
||||
|
||||
let mut paths = ConfigPaths::default();
|
||||
let mut done = false;
|
||||
let exec_path = get_executable_path(argv0.as_ref());
|
||||
if let Ok(exec_path) = exec_path.canonicalize() {
|
||||
FLOG!(
|
||||
config,
|
||||
format!("exec_path: {:?}, argv[0]: {:?}", exec_path, argv0.as_ref())
|
||||
);
|
||||
// TODO: we should determine program_name from argv0 somewhere in this file
|
||||
|
||||
// Detect if we're running right out of the CMAKE build directory
|
||||
if exec_path.starts_with(env!("CARGO_MANIFEST_DIR")) {
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
FLOG!(
|
||||
config,
|
||||
"Running out of target directory, using paths relative to CARGO_MANIFEST_DIR:\n",
|
||||
manifest_dir.display()
|
||||
);
|
||||
done = true;
|
||||
paths = ConfigPaths {
|
||||
data: manifest_dir.join("share"),
|
||||
sysconf: manifest_dir.join("etc"),
|
||||
doc: manifest_dir.join("user_doc/html"),
|
||||
bin: Some(exec_path.parent().unwrap().to_owned()),
|
||||
locale: Some(manifest_dir.join("share/locale")),
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// The next check is that we are in a relocatable directory tree
|
||||
if exec_path.ends_with("bin/fish") {
|
||||
let base_path = exec_path.parent().unwrap().parent().unwrap();
|
||||
#[cfg(feature = "installable")]
|
||||
let data = base_path.join("share/fish/install");
|
||||
#[cfg(not(feature = "installable"))]
|
||||
let data = base_path.join("share/fish");
|
||||
let locale =
|
||||
(!cfg!(feature = "installable")).then(|| base_path.join("share/locale"));
|
||||
paths = ConfigPaths {
|
||||
// One obvious path is ~/.local (with fish in ~/.local/bin/).
|
||||
// If we picked ~/.local/share/fish as our data path,
|
||||
// we would install there and erase history.
|
||||
// So let's isolate us a bit more.
|
||||
data,
|
||||
sysconf: base_path.join("etc/fish"),
|
||||
doc: base_path.join("share/doc/fish"),
|
||||
bin: Some(base_path.join("bin")),
|
||||
locale,
|
||||
}
|
||||
} else if exec_path.ends_with("fish") {
|
||||
FLOG!(
|
||||
config,
|
||||
"'fish' not in a 'bin/', trying paths relative to source tree"
|
||||
);
|
||||
let base_path = exec_path.parent().unwrap();
|
||||
#[cfg(feature = "installable")]
|
||||
let data = base_path.join("share/install");
|
||||
#[cfg(not(feature = "installable"))]
|
||||
let data = base_path.join("share");
|
||||
let locale = Some(data.join("locale"));
|
||||
|
||||
paths = ConfigPaths {
|
||||
data,
|
||||
sysconf: base_path.join("etc"),
|
||||
doc: base_path.join("user_doc/html"),
|
||||
bin: Some(base_path.to_path_buf()),
|
||||
locale,
|
||||
}
|
||||
}
|
||||
|
||||
if paths.data.exists() && paths.sysconf.exists() {
|
||||
// The docs dir may not exist; in that case fall back to the compiled in path.
|
||||
if !paths.doc.exists() {
|
||||
paths.doc = PathBuf::from(DOC_DIR);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !done {
|
||||
// Fall back to what got compiled in.
|
||||
let data = if cfg!(feature = "installable") {
|
||||
let Some(home) = env::get_home() else {
|
||||
FLOG!(
|
||||
error,
|
||||
"Cannot find home directory and will refuse to read configuration.\n",
|
||||
"Consider installing into a directory tree with `fish --install=PATH`."
|
||||
);
|
||||
return paths;
|
||||
};
|
||||
|
||||
PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
} else {
|
||||
Path::new(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
};
|
||||
let bin = if cfg!(feature = "installable") {
|
||||
exec_path.parent().map(|x| x.to_path_buf())
|
||||
} else {
|
||||
Some(PathBuf::from(BIN_DIR))
|
||||
};
|
||||
let locale = if cfg!(feature = "installable") {
|
||||
None
|
||||
} else {
|
||||
Some(PathBuf::from(LOCALE_DIR))
|
||||
};
|
||||
|
||||
FLOG!(config, "Using compiled in paths:");
|
||||
paths = ConfigPaths {
|
||||
data,
|
||||
sysconf: Path::new(SYSCONF_DIR).join("fish"),
|
||||
doc: DOC_DIR.into(),
|
||||
bin,
|
||||
locale,
|
||||
}
|
||||
}
|
||||
|
||||
FLOGF!(
|
||||
config,
|
||||
"determine_config_directory_paths() results:\npaths.data: %ls\npaths.sysconf: \
|
||||
%ls\npaths.doc: %ls\npaths.bin: %ls\npaths.locale: %ls",
|
||||
paths.data.display().to_string(),
|
||||
paths.sysconf.display().to_string(),
|
||||
paths.doc.display().to_string(),
|
||||
paths
|
||||
.bin
|
||||
.clone()
|
||||
.map(|x| x.display().to_string())
|
||||
.unwrap_or("|not found|".to_string()),
|
||||
paths
|
||||
.locale
|
||||
.clone()
|
||||
.map(|x| x.display().to_string())
|
||||
.unwrap_or("|not found|".to_string()),
|
||||
);
|
||||
|
||||
paths
|
||||
}
|
||||
2
src/env/mod.rs
vendored
2
src/env/mod.rs
vendored
@@ -1,6 +1,8 @@
|
||||
mod config_paths;
|
||||
pub mod environment;
|
||||
mod environment_impl;
|
||||
pub mod var;
|
||||
pub use config_paths::CONFIG_PATHS;
|
||||
|
||||
use crate::common::ToCString;
|
||||
pub use environment::*;
|
||||
|
||||
9
src/env/var.rs
vendored
9
src/env/var.rs
vendored
@@ -50,10 +50,11 @@ fn from(val: EnvMode) -> Self {
|
||||
/// env_init.
|
||||
#[derive(Default)]
|
||||
pub struct ConfigPaths {
|
||||
pub data: PathBuf, // e.g., /usr/local/share
|
||||
pub sysconf: PathBuf, // e.g., /usr/local/etc
|
||||
pub doc: PathBuf, // e.g., /usr/local/share/doc/fish
|
||||
pub bin: Option<PathBuf>, // e.g., /usr/local/bin
|
||||
pub data: PathBuf, // e.g., /usr/local/share
|
||||
pub sysconf: PathBuf, // e.g., /usr/local/etc
|
||||
pub doc: PathBuf, // e.g., /usr/local/share/doc/fish
|
||||
pub bin: Option<PathBuf>, // e.g., /usr/local/bin
|
||||
pub locale: Option<PathBuf>, // e.g., /usr/local/share/locale
|
||||
}
|
||||
|
||||
/// A collection of status and pipestatus.
|
||||
|
||||
@@ -554,19 +554,16 @@ fn init_curses(vars: &EnvStack) {
|
||||
if curses::setup(None, |term| apply_term_hacks(vars, term)).is_none() {
|
||||
if is_interactive_session() {
|
||||
let term = vars.get_unless_empty(L!("TERM")).map(|v| v.as_string());
|
||||
// We do not warn for xterm-256color at all, we know that one.
|
||||
if term != Some("xterm-256color".into()) {
|
||||
if let Some(term) = term {
|
||||
FLOG!(
|
||||
warning,
|
||||
wgettext_fmt!("Could not set up terminal for $TERM '%ls'. Falling back to hardcoded xterm-256color values", term)
|
||||
);
|
||||
} else {
|
||||
FLOG!(
|
||||
warning,
|
||||
wgettext!("Could not set up terminal because $TERM is unset. Falling back to hardcoded xterm-256color values")
|
||||
);
|
||||
}
|
||||
if let Some(term) = term {
|
||||
FLOG!(
|
||||
term_support,
|
||||
wgettext_fmt!("Could not set up terminal for $TERM '%ls'. Falling back to hardcoded xterm-256color values", term)
|
||||
);
|
||||
} else {
|
||||
FLOG!(
|
||||
term_support,
|
||||
wgettext!("Could not set up terminal because $TERM is unset. Falling back to hardcoded xterm-256color values")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -801,7 +801,7 @@ fn save(&mut self, directory: &wstr) -> bool {
|
||||
{
|
||||
let mut times: [libc::timespec; 2] = unsafe { std::mem::zeroed() };
|
||||
times[0].tv_nsec = libc::UTIME_OMIT; // don't change ctime
|
||||
if unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut times[1]) } != 0 {
|
||||
if unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, &mut times[1]) } == 0 {
|
||||
unsafe {
|
||||
libc::futimens(private_fd.as_raw_fd(), ×[0]);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
print_exit_warning_for_jobs, InternalProc, Job, JobGroupRef, Pid, ProcStatus, Process,
|
||||
ProcessType, TtyTransfer,
|
||||
};
|
||||
use crate::reader::{reader_run_count, restore_term_mode};
|
||||
use crate::reader::{reader_run_count, safe_restore_term_mode};
|
||||
use crate::redirection::{dup2_list_resolve_chain, Dup2List};
|
||||
use crate::threads::{iothread_perform_cant_wait, is_forked_child};
|
||||
use crate::trace::trace_if_enabled_with_args;
|
||||
@@ -437,7 +437,7 @@ fn launch_process_nofork(vars: &EnvStack, p: &Process) -> ! {
|
||||
let actual_cmd = wcs2zstring(&p.actual_cmd);
|
||||
|
||||
// Ensure the terminal modes are what they were before we changed them.
|
||||
restore_term_mode(false);
|
||||
safe_restore_term_mode(false);
|
||||
// Bounce to launch_process. This never returns.
|
||||
safe_launch_process(p, &actual_cmd, &argv, &*envp);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ pub fn all_categories() -> Vec<&'static category_t> {
|
||||
(char_encoding, "char-encoding", "Character encoding issues");
|
||||
|
||||
(history, "history", "Command history events");
|
||||
(history_file, "history-file", "Reading/Writing the history file", true);
|
||||
(history_file, "history-file", "Reading/Writing the history file");
|
||||
|
||||
(profile_history, "profile-history", "History performance measurements");
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ pub enum FeatureFlag {
|
||||
|
||||
/// Whether keyboard protocols (kitty's CSI x u, xterm's modifyOtherKeys) are used
|
||||
keyboard_protocols,
|
||||
|
||||
/// Whether to write OSC 133 prompt markers
|
||||
mark_prompt,
|
||||
}
|
||||
|
||||
struct Features {
|
||||
@@ -118,6 +121,14 @@ pub struct FeatureMetadata {
|
||||
default_value: true,
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::mark_prompt,
|
||||
name: L!("mark-prompt"),
|
||||
groups: L!("4.0"),
|
||||
description: L!("Write OSC 133 prompt markers to the terminal"),
|
||||
default_value: true,
|
||||
read_only: false,
|
||||
},
|
||||
];
|
||||
|
||||
thread_local!(
|
||||
@@ -180,6 +191,7 @@ const fn new() -> Self {
|
||||
AtomicBool::new(METADATA[4].default_value),
|
||||
AtomicBool::new(METADATA[5].default_value),
|
||||
AtomicBool::new(METADATA[6].default_value),
|
||||
AtomicBool::new(METADATA[7].default_value),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use libc::{fchmod, fchown, flock, LOCK_EX, LOCK_SH, LOCK_UN};
|
||||
use libc::{fchmod, fchown, flock, EINTR, LOCK_EX, LOCK_SH, LOCK_UN};
|
||||
use lru::LruCache;
|
||||
use nix::{fcntl::OFlag, sys::stat::Mode};
|
||||
use rand::Rng;
|
||||
@@ -1353,8 +1353,15 @@ unsafe fn maybe_lock_file(file: &mut File, lock_type: libc::c_int) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
let start_time = SystemTime::now();
|
||||
let retval = unsafe { flock(raw_fd, lock_type) };
|
||||
let (ok, start_time) = loop {
|
||||
let start_time = SystemTime::now();
|
||||
if unsafe { flock(raw_fd, lock_type) } != -1 {
|
||||
break (true, start_time);
|
||||
}
|
||||
if errno::errno().0 != EINTR {
|
||||
break (false, start_time);
|
||||
}
|
||||
};
|
||||
if let Ok(duration) = start_time.elapsed() {
|
||||
if duration > Duration::from_millis(250) {
|
||||
FLOG!(
|
||||
@@ -1367,7 +1374,7 @@ unsafe fn maybe_lock_file(file: &mut File, lock_type: libc::c_int) -> bool {
|
||||
ABANDONED_LOCKING.store(true);
|
||||
}
|
||||
}
|
||||
retval != -1
|
||||
ok
|
||||
}
|
||||
|
||||
/// Unlock a history file.
|
||||
|
||||
124
src/input.rs
124
src/input.rs
@@ -3,8 +3,11 @@
|
||||
use crate::env::{Environment, CURSES_INITIALIZED};
|
||||
use crate::event;
|
||||
use crate::flog::FLOG;
|
||||
#[allow(unused_imports)]
|
||||
use crate::future::IsSomeAnd;
|
||||
use crate::input_common::{
|
||||
CharEvent, CharInputStyle, InputData, InputEventQueuer, ReadlineCmd, R_END_INPUT_FUNCTIONS,
|
||||
match_key_event_to_key, CharEvent, CharInputStyle, InputData, InputEventQueuer, KeyEvent,
|
||||
KeyMatchQuality, ReadlineCmd, R_END_INPUT_FUNCTIONS,
|
||||
};
|
||||
use crate::key::{self, canonicalize_raw_escapes, ctrl, Key, Modifiers};
|
||||
use crate::proc::job_reap;
|
||||
@@ -16,6 +19,7 @@
|
||||
use crate::wchar::prelude::*;
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use std::ffi::CString;
|
||||
use std::mem;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Mutex, MutexGuard,
|
||||
@@ -429,7 +433,7 @@ fn select_interrupted(&mut self) {
|
||||
if reader_reading_interrupted(self) != 0 {
|
||||
let vintr = shell_modes().c_cc[libc::VINTR];
|
||||
if vintr != 0 {
|
||||
self.push_front(CharEvent::from_key(Key::from_single_byte(vintr)));
|
||||
self.push_front(CharEvent::from_key(KeyEvent::from_single_byte(vintr)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -509,14 +513,19 @@ fn next(&mut self) -> CharEvent {
|
||||
|
||||
/// Check if the next event is the given character. This advances the index on success only.
|
||||
/// If `escaped` is set, then return false if this (or any other) character had a timeout.
|
||||
fn next_is_char(&mut self, style: &KeyNameStyle, key: Key, escaped: bool) -> bool {
|
||||
fn next_is_char(
|
||||
&mut self,
|
||||
style: &KeyNameStyle,
|
||||
key: Key,
|
||||
escaped: bool,
|
||||
) -> Option<KeyMatchQuality> {
|
||||
assert!(
|
||||
self.idx <= self.peeked.len(),
|
||||
"Index must not be larger than dequeued event count"
|
||||
);
|
||||
// See if we had a timeout already.
|
||||
if escaped && self.had_timeout {
|
||||
return false;
|
||||
return None;
|
||||
}
|
||||
// Grab a new event if we have exhausted what we have already peeked.
|
||||
// Use either readch or readch_timed, per our param.
|
||||
@@ -527,7 +536,7 @@ fn next_is_char(&mut self, style: &KeyNameStyle, key: Key, escaped: bool) -> boo
|
||||
Some(evt) => evt,
|
||||
None => {
|
||||
self.had_timeout = true;
|
||||
return false;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -536,7 +545,7 @@ fn next_is_char(&mut self, style: &KeyNameStyle, key: Key, escaped: bool) -> boo
|
||||
Some(evt) => evt,
|
||||
None => {
|
||||
self.had_timeout = true;
|
||||
return false;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -546,9 +555,7 @@ fn next_is_char(&mut self, style: &KeyNameStyle, key: Key, escaped: bool) -> boo
|
||||
// Now we have peeked far enough; check the event.
|
||||
// If it matches the char, then increment the index.
|
||||
let evt = &self.peeked[self.idx];
|
||||
let Some(kevt) = evt.get_key() else {
|
||||
return false;
|
||||
};
|
||||
let kevt = evt.get_key()?;
|
||||
if kevt.seq == L!("\x1b") && key.modifiers == Modifiers::ALT {
|
||||
self.idx += 1;
|
||||
self.subidx = 0;
|
||||
@@ -556,13 +563,13 @@ fn next_is_char(&mut self, style: &KeyNameStyle, key: Key, escaped: bool) -> boo
|
||||
return self.next_is_char(style, Key::from_raw(key.codepoint), true);
|
||||
}
|
||||
if *style == KeyNameStyle::Plain {
|
||||
if kevt.key == key {
|
||||
let result = match_key_event_to_key(&kevt.key, &key);
|
||||
if let Some(key_match) = &result {
|
||||
assert!(self.subidx == 0);
|
||||
self.idx += 1;
|
||||
FLOG!(reader, "matched full key", key);
|
||||
return true;
|
||||
FLOG!(reader, "matched full key", key, "kind", key_match);
|
||||
}
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
let actual_seq = kevt.seq.as_char_slice();
|
||||
if !actual_seq.is_empty() {
|
||||
@@ -582,7 +589,7 @@ fn next_is_char(&mut self, style: &KeyNameStyle, key: Key, escaped: bool) -> boo
|
||||
actual_seq.len()
|
||||
)
|
||||
);
|
||||
return true;
|
||||
return Some(KeyMatchQuality::Exact);
|
||||
}
|
||||
if key.modifiers == Modifiers::ALT && seq_char == '\x1b' {
|
||||
if self.subidx + 1 == actual_seq.len() {
|
||||
@@ -602,11 +609,11 @@ fn next_is_char(&mut self, style: &KeyNameStyle, key: Key, escaped: bool) -> boo
|
||||
self.subidx = 0;
|
||||
}
|
||||
FLOG!(reader, format!("matched {key} against raw escape sequence"));
|
||||
return true;
|
||||
return Some(KeyMatchQuality::Exact);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
None
|
||||
}
|
||||
|
||||
/// Consume all events up to the current index.
|
||||
@@ -633,7 +640,12 @@ pub fn restart(&mut self) {
|
||||
}
|
||||
|
||||
/// Return true if this `peeker` matches a given sequence of char events given by `str`.
|
||||
fn try_peek_sequence(&mut self, style: &KeyNameStyle, seq: &[Key]) -> bool {
|
||||
fn try_peek_sequence(
|
||||
&mut self,
|
||||
style: &KeyNameStyle,
|
||||
seq: &[Key],
|
||||
quality: &mut Vec<KeyMatchQuality>,
|
||||
) -> bool {
|
||||
assert!(
|
||||
!seq.is_empty(),
|
||||
"Empty sequence passed to try_peek_sequence"
|
||||
@@ -643,9 +655,10 @@ fn try_peek_sequence(&mut self, style: &KeyNameStyle, seq: &[Key]) -> bool {
|
||||
// If we just read an escape, we need to add a timeout for the next char,
|
||||
// to distinguish between the actual escape key and an "alt"-modifier.
|
||||
let escaped = *style != KeyNameStyle::Plain && prev == Key::from_raw(key::Escape);
|
||||
if !self.next_is_char(style, *key, escaped) {
|
||||
let Some(spec) = self.next_is_char(style, *key, escaped) else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
quality.push(spec);
|
||||
prev = *key;
|
||||
}
|
||||
if self.subidx != 0 {
|
||||
@@ -662,16 +675,24 @@ fn try_peek_sequence(&mut self, style: &KeyNameStyle, seq: &[Key]) -> bool {
|
||||
/// user's mapping list, then the preset list.
|
||||
/// Return none if nothing matches, or if we may have matched a longer sequence but it was
|
||||
/// interrupted by a readline event.
|
||||
pub fn find_mapping(
|
||||
pub fn find_mapping<'a>(
|
||||
&mut self,
|
||||
vars: &dyn Environment,
|
||||
ip: &InputMappingSet,
|
||||
ip: &'a InputMappingSet,
|
||||
) -> Option<InputMapping> {
|
||||
let mut generic: Option<&InputMapping> = None;
|
||||
let bind_mode = input_get_bind_mode(vars);
|
||||
let mut escape: Option<&InputMapping> = None;
|
||||
|
||||
struct MatchedMapping<'a> {
|
||||
mapping: &'a InputMapping,
|
||||
quality: Vec<KeyMatchQuality>,
|
||||
idx: usize,
|
||||
subidx: usize,
|
||||
}
|
||||
|
||||
let mut deferred: Option<MatchedMapping<'a>> = None;
|
||||
|
||||
let ml = ip.mapping_list.iter().chain(ip.preset_mapping_list.iter());
|
||||
let mut quality = vec![];
|
||||
for m in ml {
|
||||
if m.mode != bind_mode {
|
||||
continue;
|
||||
@@ -679,24 +700,41 @@ pub fn find_mapping(
|
||||
|
||||
// Defer generic mappings until the end.
|
||||
if m.is_generic() {
|
||||
if generic.is_none() {
|
||||
generic = Some(m);
|
||||
if deferred.is_none() {
|
||||
deferred = Some(MatchedMapping {
|
||||
mapping: m,
|
||||
quality: vec![],
|
||||
idx: self.idx,
|
||||
subidx: self.subidx,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// FLOG!(reader, "trying mapping", format!("{:?}", m));
|
||||
if self.try_peek_sequence(&m.key_name_style, &m.seq) {
|
||||
// A binding for just escape should also be deferred
|
||||
// so escape sequences take precedence.
|
||||
if m.seq == vec![Key::from_raw(key::Escape)] {
|
||||
if escape.is_none() {
|
||||
escape = Some(m);
|
||||
}
|
||||
} else {
|
||||
if self.try_peek_sequence(&m.key_name_style, &m.seq, &mut quality) {
|
||||
// // A binding for just escape should also be deferred
|
||||
// // so escape sequences take precedence.
|
||||
let is_escape = m.seq == vec![Key::from_raw(key::Escape)];
|
||||
let is_perfect_match = quality
|
||||
.iter()
|
||||
.all(|key_match| *key_match == KeyMatchQuality::Exact);
|
||||
if !is_escape && is_perfect_match {
|
||||
return Some(m.clone());
|
||||
}
|
||||
if deferred
|
||||
.as_ref()
|
||||
.is_none_or(|matched| !is_escape && quality >= matched.quality)
|
||||
{
|
||||
deferred = Some(MatchedMapping {
|
||||
mapping: m,
|
||||
quality: mem::take(&mut quality),
|
||||
idx: self.idx,
|
||||
subidx: self.subidx,
|
||||
});
|
||||
}
|
||||
}
|
||||
quality.clear();
|
||||
self.restart();
|
||||
}
|
||||
if self.char_sequence_interrupted() {
|
||||
@@ -705,17 +743,13 @@ pub fn find_mapping(
|
||||
return None;
|
||||
}
|
||||
|
||||
if escape.is_some() {
|
||||
// We need to reconsume the escape.
|
||||
self.next();
|
||||
return escape.cloned();
|
||||
}
|
||||
|
||||
if generic.is_some() {
|
||||
generic.cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
deferred
|
||||
.map(|matched| {
|
||||
self.idx = matched.idx;
|
||||
self.subidx = matched.subidx;
|
||||
matched.mapping
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,7 +875,7 @@ fn read_characters_no_readline(&mut self) -> CharEvent {
|
||||
let evt_to_return: CharEvent;
|
||||
loop {
|
||||
let evt = self.readch();
|
||||
if evt.is_readline_or_command() {
|
||||
if evt.is_readline_or_command() || evt.is_check_exit() {
|
||||
saved_events.push(evt);
|
||||
} else {
|
||||
evt_to_return = evt;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
135
src/key.rs
135
src/key.rs
@@ -3,9 +3,9 @@
|
||||
use crate::{
|
||||
common::{escape_string, EscapeFlags, EscapeStringStyle},
|
||||
fallback::fish_wcwidth,
|
||||
reader::TERMINAL_MODE_ON_STARTUP,
|
||||
reader::safe_get_terminal_mode_on_startup,
|
||||
wchar::{decode_byte_from_char, prelude::*},
|
||||
wutil::{fish_is_pua, fish_wcstoi},
|
||||
wutil::fish_wcstoul,
|
||||
};
|
||||
|
||||
pub(crate) const Backspace: char = '\u{F500}'; // below ENCODE_DIRECT_BASE
|
||||
@@ -18,16 +18,19 @@
|
||||
pub(crate) const Right: char = '\u{F507}';
|
||||
pub(crate) const PageUp: char = '\u{F508}';
|
||||
pub(crate) const PageDown: char = '\u{F509}';
|
||||
pub(crate) const Home: char = '\u{F50a}';
|
||||
pub(crate) const End: char = '\u{F50b}';
|
||||
pub(crate) const Insert: char = '\u{F50c}';
|
||||
pub(crate) const Tab: char = '\u{F50d}';
|
||||
pub(crate) const Space: char = '\u{F50e}';
|
||||
pub const Invalid: char = '\u{F50f}';
|
||||
pub(crate) const Home: char = '\u{F50A}';
|
||||
pub(crate) const End: char = '\u{F50B}';
|
||||
pub(crate) const Insert: char = '\u{F50C}';
|
||||
pub(crate) const Tab: char = '\u{F50D}';
|
||||
pub(crate) const Space: char = '\u{F50E}';
|
||||
pub(crate) const Menu: char = '\u{F50F}';
|
||||
pub(crate) const PrintScreen: char = '\u{F510}';
|
||||
pub(crate) const MAX_FUNCTION_KEY: u32 = 12;
|
||||
pub(crate) fn function_key(n: u32) -> char {
|
||||
assert!((1..=12).contains(&n));
|
||||
char::from_u32(u32::from(Invalid) + n).unwrap()
|
||||
assert!((1..=MAX_FUNCTION_KEY).contains(&n));
|
||||
char::from_u32(u32::from('\u{F5FF}') - MAX_FUNCTION_KEY + (n - 1)).unwrap()
|
||||
}
|
||||
pub(crate) const Invalid: char = '\u{F5FF}';
|
||||
|
||||
const KEY_NAMES: &[(char, &wstr)] = &[
|
||||
('-', L!("minus")),
|
||||
@@ -47,6 +50,8 @@ pub(crate) fn function_key(n: u32) -> char {
|
||||
(Insert, L!("insert")),
|
||||
(Tab, L!("tab")),
|
||||
(Space, L!("space")),
|
||||
(Menu, L!("menu")),
|
||||
(PrintScreen, L!("printscreen")),
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
@@ -54,6 +59,7 @@ pub struct Modifiers {
|
||||
pub ctrl: bool,
|
||||
pub alt: bool,
|
||||
pub shift: bool,
|
||||
pub sup: bool,
|
||||
}
|
||||
|
||||
impl Modifiers {
|
||||
@@ -62,18 +68,30 @@ const fn new() -> Self {
|
||||
ctrl: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
sup: false,
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
pub(crate) const CTRL: Self = {
|
||||
let mut m = Self::new();
|
||||
m.ctrl = true;
|
||||
m
|
||||
};
|
||||
pub(crate) const ALT: Self = {
|
||||
let mut m = Self::new();
|
||||
m.alt = true;
|
||||
m
|
||||
};
|
||||
pub const SHIFT: Self = {
|
||||
let mut m = Self::new();
|
||||
m.shift = true;
|
||||
m
|
||||
};
|
||||
pub(crate) fn is_some(&self) -> bool {
|
||||
self.ctrl || self.alt || self.shift
|
||||
*self != Self::new()
|
||||
}
|
||||
pub(crate) fn is_none(&self) -> bool {
|
||||
!self.is_some()
|
||||
*self == Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,39 +102,33 @@ pub struct Key {
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub(crate) fn from_raw(codepoint: char) -> Self {
|
||||
pub(crate) const fn new(modifiers: Modifiers, codepoint: char) -> Self {
|
||||
Self {
|
||||
modifiers: Modifiers::default(),
|
||||
modifiers,
|
||||
codepoint,
|
||||
}
|
||||
}
|
||||
pub(crate) fn from_raw(codepoint: char) -> Self {
|
||||
Self::new(Modifiers::default(), codepoint)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn ctrl(codepoint: char) -> Key {
|
||||
let mut modifiers = Modifiers::new();
|
||||
modifiers.ctrl = true;
|
||||
Key {
|
||||
modifiers,
|
||||
codepoint,
|
||||
}
|
||||
Key::new(modifiers, codepoint)
|
||||
}
|
||||
|
||||
pub(crate) const fn alt(codepoint: char) -> Key {
|
||||
let mut modifiers = Modifiers::new();
|
||||
modifiers.alt = true;
|
||||
Key {
|
||||
modifiers,
|
||||
codepoint,
|
||||
}
|
||||
Key::new(modifiers, codepoint)
|
||||
}
|
||||
|
||||
pub(crate) const fn shift(codepoint: char) -> Key {
|
||||
let mut modifiers = Modifiers::new();
|
||||
modifiers.shift = true;
|
||||
Key {
|
||||
modifiers,
|
||||
codepoint,
|
||||
}
|
||||
Key::new(modifiers, codepoint)
|
||||
}
|
||||
|
||||
impl Key {
|
||||
@@ -133,10 +145,7 @@ pub fn from_single_byte(c: u8) -> Self {
|
||||
pub fn canonicalize_control_char(c: u8) -> Option<Key> {
|
||||
let codepoint = canonicalize_keyed_control_char(char::from(c));
|
||||
if u32::from(codepoint) > 255 {
|
||||
return Some(Key {
|
||||
modifiers: Modifiers::default(),
|
||||
codepoint,
|
||||
});
|
||||
return Some(Key::from_raw(codepoint));
|
||||
}
|
||||
|
||||
if c < 32 {
|
||||
@@ -160,8 +169,10 @@ pub(crate) fn canonicalize_keyed_control_char(c: char) -> char {
|
||||
if c == ' ' {
|
||||
return Space;
|
||||
}
|
||||
if c == char::from(TERMINAL_MODE_ON_STARTUP.lock().unwrap().c_cc[VERASE]) {
|
||||
return Backspace;
|
||||
if let Some(tm) = safe_get_terminal_mode_on_startup() {
|
||||
if c == char::from(tm.c_cc[VERASE]) {
|
||||
return Backspace;
|
||||
}
|
||||
}
|
||||
if c == char::from(127) {
|
||||
// when it's not backspace
|
||||
@@ -205,19 +216,6 @@ pub(crate) fn canonicalize_key(mut key: Key) -> Result<Key, WString> {
|
||||
key.modifiers.ctrl = true;
|
||||
}
|
||||
}
|
||||
if key.modifiers.shift {
|
||||
if key.codepoint.is_ascii_alphabetic() {
|
||||
// Shift + ASCII letters is just the uppercase letter.
|
||||
key.modifiers.shift = false;
|
||||
key.codepoint = key.codepoint.to_ascii_uppercase();
|
||||
} else if !fish_is_pua(key.codepoint) {
|
||||
// Shift + any other printable character is not allowed.
|
||||
return Err(wgettext_fmt!(
|
||||
"Shift modifier is only supported on special keys and lowercase ASCII, not '%s'",
|
||||
key,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
@@ -271,6 +269,7 @@ pub(crate) fn parse_keys(value: &wstr) -> Result<Vec<Key>, WString> {
|
||||
_ if modifier == "ctrl" => modifiers.ctrl = true,
|
||||
_ if modifier == "alt" => modifiers.alt = true,
|
||||
_ if modifier == "shift" => modifiers.shift = true,
|
||||
_ if modifier == "super" => modifiers.sup = true,
|
||||
_ => {
|
||||
return Err(wgettext_fmt!(
|
||||
"unknown modifier '%s' in '%s'",
|
||||
@@ -286,25 +285,22 @@ pub(crate) fn parse_keys(value: &wstr) -> Result<Vec<Key>, WString> {
|
||||
.find_map(|(codepoint, name)| (name == key_name).then_some(*codepoint))
|
||||
.or_else(|| (key_name.len() == 1).then(|| key_name.as_char_slice()[0]));
|
||||
let key = if let Some(codepoint) = codepoint {
|
||||
canonicalize_key(Key {
|
||||
modifiers,
|
||||
codepoint,
|
||||
})?
|
||||
canonicalize_key(Key::new(modifiers, codepoint))?
|
||||
} else if codepoint.is_none() && key_name.starts_with('f') && key_name.len() <= 3 {
|
||||
let num = key_name.strip_prefix('f').unwrap();
|
||||
let codepoint = match fish_wcstoi(num) {
|
||||
Ok(n) if (1..=12).contains(&n) => function_key(u32::try_from(n).unwrap()),
|
||||
let codepoint = match fish_wcstoul(num) {
|
||||
Ok(n) if (1..=u64::from(MAX_FUNCTION_KEY)).contains(&n) => {
|
||||
function_key(u32::try_from(n).unwrap())
|
||||
}
|
||||
_ => {
|
||||
return Err(wgettext_fmt!(
|
||||
"only f1 through f12 are supported, not 'f%s'",
|
||||
"only f1 through f%d are supported, not 'f%s'",
|
||||
MAX_FUNCTION_KEY,
|
||||
num,
|
||||
));
|
||||
}
|
||||
};
|
||||
Key {
|
||||
modifiers,
|
||||
codepoint,
|
||||
}
|
||||
Key::new(modifiers, codepoint)
|
||||
} else {
|
||||
return Err(wgettext_fmt!(
|
||||
"cannot parse key '%s'",
|
||||
@@ -347,28 +343,6 @@ pub(crate) fn canonicalize_raw_escapes(keys: Vec<Key>) -> Vec<Key> {
|
||||
canonical
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub(crate) fn codepoint_text(&self) -> Option<char> {
|
||||
if self.modifiers.is_some() {
|
||||
return None;
|
||||
}
|
||||
let c = self.codepoint;
|
||||
if c == Space {
|
||||
return Some(' ');
|
||||
}
|
||||
if c == Enter {
|
||||
return Some('\n');
|
||||
}
|
||||
if c == Tab {
|
||||
return Some('\t');
|
||||
}
|
||||
if fish_is_pua(c) || u32::from(c) <= 27 {
|
||||
return None;
|
||||
}
|
||||
Some(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Key {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
WString::from(*self).fmt(f)
|
||||
@@ -387,7 +361,7 @@ fn from(key: Key) -> Self {
|
||||
.iter()
|
||||
.find_map(|&(codepoint, name)| (codepoint == key.codepoint).then(|| name.to_owned()))
|
||||
.or_else(|| {
|
||||
(function_key(1)..=function_key(12))
|
||||
(function_key(1)..=function_key(MAX_FUNCTION_KEY))
|
||||
.contains(&key.codepoint)
|
||||
.then(|| {
|
||||
sprintf!(
|
||||
@@ -407,6 +381,9 @@ fn from(key: Key) -> Self {
|
||||
if key.modifiers.ctrl {
|
||||
res.insert_utfstr(0, L!("ctrl-"));
|
||||
}
|
||||
if key.modifiers.sup {
|
||||
res.insert_utfstr(0, L!("super-"));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
10
src/libc.c
10
src/libc.c
@@ -1,13 +1,13 @@
|
||||
#include <locale.h>
|
||||
#include <paths.h>
|
||||
#include <paths.h> // _PATH_BSHELL
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdlib.h> // MB_CUR_MAX
|
||||
#include <sys/mount.h> // MNT_LOCAL
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
#include <sys/statvfs.h> // ST_LOCAL
|
||||
#include <unistd.h> // _CS_PATH, _PC_CASE_SENSITIVE
|
||||
|
||||
size_t C_MB_CUR_MAX() { return MB_CUR_MAX; }
|
||||
|
||||
|
||||
@@ -218,17 +218,30 @@ fn parse_util_locate_cmdsub(
|
||||
let mut last_dollar = None;
|
||||
let mut paran_begin = None;
|
||||
let mut paran_end = None;
|
||||
enum Quote {
|
||||
Real(char),
|
||||
VirtualDouble,
|
||||
}
|
||||
fn process_opening_quote(
|
||||
input: &[char],
|
||||
inout_is_quoted: &mut Option<&mut bool>,
|
||||
paran_count: i32,
|
||||
quoted_cmdsubs: &mut Vec<i32>,
|
||||
pos: usize,
|
||||
mut pos: usize,
|
||||
last_dollar: &mut Option<usize>,
|
||||
quote: char,
|
||||
quote: Quote,
|
||||
) -> Option<usize> {
|
||||
let quote = match quote {
|
||||
Quote::Real(q) => q,
|
||||
Quote::VirtualDouble => {
|
||||
pos = pos.saturating_sub(1);
|
||||
'"'
|
||||
}
|
||||
};
|
||||
let q_end = quote_end(input.into(), pos, quote)?;
|
||||
// Found a valid closing quote.
|
||||
if input[q_end] == '$' {
|
||||
// The closing quote is another quoted command substitution.
|
||||
*last_dollar = Some(q_end);
|
||||
quoted_cmdsubs.push(paran_count);
|
||||
}
|
||||
@@ -254,9 +267,9 @@ fn process_opening_quote(
|
||||
&mut quoted_cmdsubs,
|
||||
pos,
|
||||
&mut last_dollar,
|
||||
'"',
|
||||
Quote::VirtualDouble,
|
||||
)
|
||||
.unwrap_or(input.len());
|
||||
.map_or(input.len(), |pos| pos + 1);
|
||||
}
|
||||
|
||||
while pos < input.len() {
|
||||
@@ -270,7 +283,7 @@ fn process_opening_quote(
|
||||
&mut quoted_cmdsubs,
|
||||
pos,
|
||||
&mut last_dollar,
|
||||
c,
|
||||
Quote::Real(c),
|
||||
) {
|
||||
Some(q_end) => pos = q_end,
|
||||
None => break,
|
||||
@@ -307,21 +320,19 @@ fn process_opening_quote(
|
||||
if quoted_cmdsubs.last() == Some(¶n_count) {
|
||||
quoted_cmdsubs.pop();
|
||||
// Quoted command substitutions temporarily close double quotes.
|
||||
// In "foo$(bar)baz$(qux)"
|
||||
// We are here ^
|
||||
// After the ) in a quoted command substitution, we need to act as if
|
||||
// there was an invisible double quote.
|
||||
match quote_end(input.into(), pos, '"') {
|
||||
Some(q_end) => {
|
||||
// Found a valid closing quote.
|
||||
// Stop at $(qux), which is another quoted command substitution.
|
||||
if input[q_end] == '$' {
|
||||
quoted_cmdsubs.push(paran_count);
|
||||
}
|
||||
pos = q_end;
|
||||
}
|
||||
// In "foo$(bar)baz$(qux)", after the ), we need to act as if there was a double quote.
|
||||
match process_opening_quote(
|
||||
input,
|
||||
&mut inout_is_quoted,
|
||||
paran_count,
|
||||
&mut quoted_cmdsubs,
|
||||
pos,
|
||||
&mut last_dollar,
|
||||
Quote::VirtualDouble,
|
||||
) {
|
||||
Some(q_end) => pos = q_end,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
is_token_begin = is_token_delimiter(c, input.get(pos + 1).copied());
|
||||
|
||||
@@ -276,6 +276,9 @@ pub struct LibraryData {
|
||||
/// Whether we are currently interactive.
|
||||
pub is_interactive: bool,
|
||||
|
||||
/// Whether the command line is closed for modification from fish script.
|
||||
pub readonly_commandline: bool,
|
||||
|
||||
/// Whether to suppress fish_trace output. This occurs in the prompt, event handlers, and key
|
||||
/// bindings.
|
||||
pub suppress_fish_trace: bool,
|
||||
@@ -611,7 +614,7 @@ pub fn eval_node<T: Node>(
|
||||
let mut execution_context =
|
||||
ExecutionContext::new(ps.clone(), block_io.clone(), Rc::clone(&line_counter));
|
||||
|
||||
terminal_protocols_disable_ifn();
|
||||
terminal_protocols_disable_ifn(false);
|
||||
|
||||
// Check the exec count so we know if anything got executed.
|
||||
let prev_exec_count = self.libdata().exec_count;
|
||||
|
||||
86
src/path.rs
86
src/path.rs
@@ -2,12 +2,10 @@
|
||||
//! for testing if a command with a given name can be found in the PATH, and various other
|
||||
//! path-related issues.
|
||||
|
||||
use crate::common::{is_windows_subsystem_for_linux as is_wsl, wcs2osstring, wcs2zstring, WSL};
|
||||
use crate::common::{wcs2osstring, wcs2zstring};
|
||||
use crate::env::{EnvMode, EnvStack, Environment};
|
||||
use crate::expand::{expand_tilde, HOME_DIRECTORY};
|
||||
use crate::flog::{FLOG, FLOGF};
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
use crate::libc::{MNT_LOCAL, ST_LOCAL};
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wutil::{normalize_path, path_normalize_for_cd, waccess, wdirname, wstat};
|
||||
use errno::{errno, set_errno, Errno};
|
||||
@@ -15,6 +13,7 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::ErrorKind;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
/// Returns the user configuration directory for fish. If the directory or one of its parents
|
||||
@@ -308,29 +307,6 @@ fn path_get_path_core<S: AsRef<wstr>>(cmd: &wstr, pathsv: &[S]) -> GetPathResult
|
||||
return GetPathResult::new(test_path(cmd).err(), cmd.to_owned());
|
||||
}
|
||||
|
||||
// WSLv1/WSLv2 tack on the entire Windows PATH to the end of the PATH environment variable, and
|
||||
// accessing these paths from WSL binaries is pathalogically slow. We also don't expect to find
|
||||
// any "normal" nix binaries under these paths, so we can skip them unless we are executing bins
|
||||
// with Windows-ish names. We try to keep paths manually added to $fish_user_paths by only
|
||||
// chopping off entries after the last "normal" PATH entry.
|
||||
let pathsv = if is_wsl(WSL::Any) && !cmd.contains('.') {
|
||||
let win_path_count = pathsv
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|p| {
|
||||
let p = p.as_ref();
|
||||
p.starts_with("/mnt/")
|
||||
&& p.chars()
|
||||
.nth("/mnt/x".len())
|
||||
.map(|c| c == '/')
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.count();
|
||||
&pathsv[..pathsv.len() - win_path_count]
|
||||
} else {
|
||||
pathsv
|
||||
};
|
||||
|
||||
let mut best = noent_res;
|
||||
for next_path in pathsv {
|
||||
let next_path: &wstr = next_path.as_ref();
|
||||
@@ -699,10 +675,11 @@ fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
let narrow = wcs2zstring(path);
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let mut buf: libc::statfs = unsafe { std::mem::zeroed() };
|
||||
if unsafe { libc::statfs(narrow.as_ptr(), &mut buf) } < 0 {
|
||||
let mut buf = MaybeUninit::uninit();
|
||||
if unsafe { libc::statfs(narrow.as_ptr(), buf.as_mut_ptr()) } < 0 {
|
||||
return DirRemoteness::unknown;
|
||||
}
|
||||
let buf = unsafe { buf.assume_init() };
|
||||
// Linux has constants for these like NFS_SUPER_MAGIC, SMB_SUPER_MAGIC, CIFS_MAGIC_NUMBER but
|
||||
// these are in varying headers. Simply hard code them.
|
||||
// Note that we treat FUSE filesystems as remote, which means we lock less on such filesystems.
|
||||
@@ -732,38 +709,49 @@ fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
let st_local = ST_LOCAL();
|
||||
if st_local != 0 {
|
||||
// ST_LOCAL is a flag to statvfs, which is itself standardized.
|
||||
// In practice the only system to use this path is NetBSD.
|
||||
let mut buf: libc::statvfs = unsafe { std::mem::zeroed() };
|
||||
if unsafe { libc::statvfs(narrow.as_ptr(), &mut buf) } < 0 {
|
||||
fn remoteness_via_statfs<StatFS, Flags>(
|
||||
statfn: unsafe extern "C" fn(*const libc::c_char, *mut StatFS) -> libc::c_int,
|
||||
flagsfn: fn(&StatFS) -> Flags,
|
||||
is_local_flag: u64,
|
||||
path: &std::ffi::CStr,
|
||||
) -> DirRemoteness
|
||||
where
|
||||
u64: From<Flags>,
|
||||
{
|
||||
if is_local_flag == 0 {
|
||||
return DirRemoteness::unknown;
|
||||
}
|
||||
// statvfs::f_flag is `unsigned long`, which is 4-bytes on most 32-bit targets.
|
||||
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
|
||||
return if u64::from(buf.f_flag) & st_local != 0 {
|
||||
DirRemoteness::local
|
||||
} else {
|
||||
DirRemoteness::remote
|
||||
};
|
||||
}
|
||||
let mnt_local = MNT_LOCAL();
|
||||
if mnt_local != 0 {
|
||||
let mut buf: libc::statvfs = unsafe { std::mem::zeroed() };
|
||||
if unsafe { libc::statvfs(narrow.as_ptr(), &mut buf) } < 0 {
|
||||
let mut buf = MaybeUninit::uninit();
|
||||
if unsafe { (statfn)(path.as_ptr(), buf.as_mut_ptr()) } < 0 {
|
||||
return DirRemoteness::unknown;
|
||||
}
|
||||
let buf = unsafe { buf.assume_init() };
|
||||
// statfs::f_flag is hard-coded as 64-bits on 32/64-bit FreeBSD but it's a (4-byte)
|
||||
// long on 32-bit NetBSD.. and always 4-bytes on macOS (even on 64-bit builds).
|
||||
#[allow(clippy::useless_conversion)]
|
||||
return if u64::from(buf.f_flag) & mnt_local != 0 {
|
||||
if u64::from((flagsfn)(&buf)) & is_local_flag != 0 {
|
||||
DirRemoteness::local
|
||||
} else {
|
||||
DirRemoteness::remote
|
||||
};
|
||||
}
|
||||
}
|
||||
DirRemoteness::unknown
|
||||
// ST_LOCAL is a flag to statvfs, which is itself standardized.
|
||||
// In practice the only system to define it is NetBSD.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
let remoteness = remoteness_via_statfs(
|
||||
libc::statvfs,
|
||||
|stat: &libc::statvfs| stat.f_flag,
|
||||
crate::libc::ST_LOCAL(),
|
||||
&narrow,
|
||||
);
|
||||
#[cfg(not(target_os = "netbsd"))]
|
||||
let remoteness = remoteness_via_statfs(
|
||||
libc::statfs,
|
||||
|stat: &libc::statfs| stat.f_flags,
|
||||
crate::libc::MNT_LOCAL(),
|
||||
&narrow,
|
||||
);
|
||||
remoteness
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
141
src/reader.rs
141
src/reader.rs
@@ -34,8 +34,7 @@
|
||||
use std::rc::Rc;
|
||||
#[cfg(target_has_atomic = "64")]
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::atomic::{AtomicI32, AtomicU32, AtomicU8};
|
||||
use std::sync::atomic::{AtomicI32, AtomicPtr, AtomicU32, AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -67,6 +66,7 @@
|
||||
use crate::flog::{FLOG, FLOGF};
|
||||
#[allow(unused_imports)]
|
||||
use crate::future::IsSomeAnd;
|
||||
use crate::future_feature_flags::{feature_test, FeatureFlag};
|
||||
use crate::global_safety::RelaxedAtomicBool;
|
||||
use crate::highlight::{
|
||||
autosuggest_validate_from_history, highlight_shell, HighlightRole, HighlightSpec,
|
||||
@@ -152,9 +152,9 @@ enum ExitState {
|
||||
pub static SHELL_MODES: Lazy<Mutex<libc::termios>> =
|
||||
Lazy::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
|
||||
|
||||
/// Mode on startup, which we restore on exit.
|
||||
pub static TERMINAL_MODE_ON_STARTUP: Lazy<Mutex<libc::termios>> =
|
||||
Lazy::new(|| Mutex::new(unsafe { std::mem::zeroed() }));
|
||||
/// The valid terminal modes on startup. This is set once and not modified after.
|
||||
/// Warning: this is read from the SIGTERM handler! Hence the raw global.
|
||||
static TERMINAL_MODE_ON_STARTUP: AtomicPtr<libc::termios> = AtomicPtr::new(std::ptr::null_mut());
|
||||
|
||||
/// Mode we use to execute programs.
|
||||
static TTY_MODES_FOR_EXTERNAL_CMDS: Lazy<Mutex<libc::termios>> =
|
||||
@@ -171,6 +171,12 @@ enum ExitState {
|
||||
/// This is set from a signal handler.
|
||||
static SIGHUP_RECEIVED: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
||||
|
||||
// Get the terminal mode on startup. This is "safe" because it's async-signal safe.
|
||||
pub fn safe_get_terminal_mode_on_startup() -> Option<&'static libc::termios> {
|
||||
// Safety: set atomically and not modified after.
|
||||
unsafe { TERMINAL_MODE_ON_STARTUP.load(Ordering::Acquire).as_ref() }
|
||||
}
|
||||
|
||||
/// A singleton snapshot of the reader state. This is factored out for thread-safety reasons:
|
||||
/// it may be fetched on a background thread.
|
||||
fn commandline_state_snapshot() -> MutexGuard<'static, CommandlineState> {
|
||||
@@ -659,13 +665,15 @@ fn read_i(parser: &Parser) -> i32 {
|
||||
data.command_line.clear();
|
||||
data.update_buff_pos(EditableLineTag::Commandline, None);
|
||||
data.command_line_changed(EditableLineTag::Commandline);
|
||||
// OSC 133 "Command start"
|
||||
write!(
|
||||
BufferedOuputter::new(&mut Outputter::stdoutput().borrow_mut()),
|
||||
"\x1b]133;C;cmdline_url={}\x07",
|
||||
escape_string(&command, EscapeStringStyle::Url),
|
||||
)
|
||||
.unwrap();
|
||||
if feature_test(FeatureFlag::mark_prompt) {
|
||||
// OSC 133 "Command start"
|
||||
write!(
|
||||
BufferedOuputter::new(&mut Outputter::stdoutput().borrow_mut()),
|
||||
"\x1b]133;C;cmdline_url={}\x07",
|
||||
escape_string(&command, EscapeStringStyle::Url),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
event::fire_generic(parser, L!("fish_preexec").to_owned(), vec![command.clone()]);
|
||||
let eval_res = reader_run_command(parser, &command);
|
||||
signal_clear_cancel();
|
||||
@@ -678,12 +686,14 @@ fn read_i(parser: &Parser) -> i32 {
|
||||
parser.libdata_mut().exit_current_script = false;
|
||||
|
||||
// OSC 133 "Command finished"
|
||||
write!(
|
||||
BufferedOuputter::new(&mut Outputter::stdoutput().borrow_mut()),
|
||||
"\x1b]133;D;{}\x07",
|
||||
parser.get_last_status()
|
||||
)
|
||||
.unwrap();
|
||||
if feature_test(FeatureFlag::mark_prompt) {
|
||||
write!(
|
||||
BufferedOuputter::new(&mut Outputter::stdoutput().borrow_mut()),
|
||||
"\x1b]133;D;{}\x07",
|
||||
parser.get_last_status()
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
event::fire_generic(parser, L!("fish_postexec").to_owned(), vec![command]);
|
||||
// Allow any pending history items to be returned in the history array.
|
||||
data.history.resolve_pending();
|
||||
@@ -813,8 +823,15 @@ fn read_ni(parser: &Parser, fd: RawFd, io: &IoChain) -> i32 {
|
||||
/// Initialize the reader.
|
||||
pub fn reader_init(will_restore_foreground_pgroup: bool) {
|
||||
// Save the initial terminal mode.
|
||||
let mut terminal_mode_on_startup = TERMINAL_MODE_ON_STARTUP.lock().unwrap();
|
||||
unsafe { libc::tcgetattr(STDIN_FILENO, &mut *terminal_mode_on_startup) };
|
||||
// Note this field is read by a signal handler, so do it atomically, with a leaked mode.
|
||||
let mut terminal_mode_on_startup = unsafe { std::mem::zeroed::<libc::termios>() };
|
||||
let ret = unsafe { libc::tcgetattr(libc::STDIN_FILENO, &mut terminal_mode_on_startup) };
|
||||
// TODO: rationalize behavior if initial tcgetattr() fails.
|
||||
if ret == 0 {
|
||||
// Must be mut because AtomicPtr doesn't have const variant.
|
||||
let leaked: *mut libc::termios = Box::leak(Box::new(terminal_mode_on_startup));
|
||||
TERMINAL_MODE_ON_STARTUP.store(leaked, Ordering::Release);
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
assert!(AT_EXIT.get().is_none());
|
||||
@@ -826,7 +843,7 @@ pub fn reader_init(will_restore_foreground_pgroup: bool) {
|
||||
|
||||
// Set the mode used for program execution, initialized to the current mode.
|
||||
let mut tty_modes_for_external_cmds = TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap();
|
||||
*tty_modes_for_external_cmds = *terminal_mode_on_startup;
|
||||
*tty_modes_for_external_cmds = terminal_mode_on_startup;
|
||||
term_fix_external_modes(&mut tty_modes_for_external_cmds);
|
||||
|
||||
// Disable flow control by default.
|
||||
@@ -838,7 +855,6 @@ pub fn reader_init(will_restore_foreground_pgroup: bool) {
|
||||
|
||||
term_fix_modes(&mut shell_modes());
|
||||
|
||||
drop(terminal_mode_on_startup);
|
||||
drop(tty_modes_for_external_cmds);
|
||||
|
||||
// Set up our fixed terminal modes once,
|
||||
@@ -850,9 +866,11 @@ pub fn reader_init(will_restore_foreground_pgroup: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(pca): this is run in our "AT_EXIT" handler from a SIGTERM handler.
|
||||
// It must be made async-signal-safe (or not invoked).
|
||||
pub fn reader_deinit(in_signal_handler: bool, restore_foreground_pgroup: bool) {
|
||||
restore_term_mode(in_signal_handler);
|
||||
crate::input_common::terminal_protocols_disable_ifn();
|
||||
safe_restore_term_mode(in_signal_handler);
|
||||
crate::input_common::terminal_protocols_disable_ifn(in_signal_handler);
|
||||
if restore_foreground_pgroup {
|
||||
restore_term_foreground_process_group_for_exit();
|
||||
}
|
||||
@@ -861,20 +879,15 @@ pub fn reader_deinit(in_signal_handler: bool, restore_foreground_pgroup: bool) {
|
||||
/// Restore the term mode if we own the terminal and are interactive (#8705).
|
||||
/// It's important we do this before restore_foreground_process_group,
|
||||
/// otherwise we won't think we own the terminal.
|
||||
pub fn restore_term_mode(in_signal_handler: bool) {
|
||||
/// THIS FUNCTION IS CALLED FROM A SIGNAL HANDLER. IT MUST BE ASYNC-SIGNAL-SAFE.
|
||||
pub fn safe_restore_term_mode(in_signal_handler: bool) {
|
||||
if !is_interactive_session() || unsafe { libc::getpgrp() != libc::tcgetpgrp(STDIN_FILENO) } {
|
||||
return;
|
||||
}
|
||||
|
||||
if unsafe {
|
||||
libc::tcsetattr(
|
||||
STDIN_FILENO,
|
||||
TCSANOW,
|
||||
&*TERMINAL_MODE_ON_STARTUP.lock().unwrap(),
|
||||
) == -1
|
||||
} && errno().0 == EIO
|
||||
{
|
||||
redirect_tty_output(in_signal_handler);
|
||||
if let Some(modes) = safe_get_terminal_mode_on_startup() {
|
||||
if unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, modes) == -1 } && errno().0 == EIO {
|
||||
redirect_tty_output(in_signal_handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -955,6 +968,9 @@ pub fn reader_schedule_prompt_repaint() {
|
||||
}
|
||||
|
||||
pub fn reader_execute_readline_cmd(parser: &Parser, ch: CharEvent) {
|
||||
if parser.libdata().readonly_commandline {
|
||||
return;
|
||||
}
|
||||
if let Some(data) = current_data() {
|
||||
let mut data = Reader { parser, data };
|
||||
let CharEvent::Readline(readline_cmd_evt) = &ch else {
|
||||
@@ -977,7 +993,7 @@ pub fn reader_execute_readline_cmd(parser: &Parser, ch: CharEvent) {
|
||||
data.rls = Some(ReadlineLoopState::new());
|
||||
}
|
||||
data.save_screen_state();
|
||||
data.handle_char_event(Some(ch));
|
||||
let _ = data.handle_char_event(Some(ch));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1033,7 +1049,10 @@ pub fn commandline_get_state(sync: bool) -> CommandlineState {
|
||||
|
||||
/// Set the command line text and position. This may be called on a background thread; the reader
|
||||
/// will pick it up when it is done executing.
|
||||
pub fn commandline_set_buffer(text: Option<WString>, cursor_pos: Option<usize>) {
|
||||
pub fn commandline_set_buffer(parser: &Parser, text: Option<WString>, cursor_pos: Option<usize>) {
|
||||
if parser.libdata().readonly_commandline {
|
||||
return;
|
||||
}
|
||||
{
|
||||
let mut state = commandline_state_snapshot();
|
||||
if let Some(text) = text {
|
||||
@@ -1044,7 +1063,10 @@ pub fn commandline_set_buffer(text: Option<WString>, cursor_pos: Option<usize>)
|
||||
current_data().map(|data| data.apply_commandline_state_changes());
|
||||
}
|
||||
|
||||
pub fn commandline_set_search_field(text: WString, cursor_pos: Option<usize>) {
|
||||
pub fn commandline_set_search_field(parser: &Parser, text: WString, cursor_pos: Option<usize>) {
|
||||
if parser.libdata().readonly_commandline {
|
||||
return;
|
||||
}
|
||||
{
|
||||
let mut state = commandline_state_snapshot();
|
||||
assert!(state.search_field.is_some());
|
||||
@@ -3696,8 +3718,11 @@ fn clear_pager(&mut self) {
|
||||
|
||||
fn get_selection(&self) -> Option<Range<usize>> {
|
||||
let selection = self.selection?;
|
||||
let start = selection.start;
|
||||
let start = std::cmp::min(selection.start, self.command_line.len());
|
||||
let end = std::cmp::min(selection.stop, self.command_line.len());
|
||||
if start == end {
|
||||
return None;
|
||||
}
|
||||
Some(start..end)
|
||||
}
|
||||
|
||||
@@ -3777,6 +3802,7 @@ fn pager_selection_changed(&mut self) {
|
||||
&self.cycle_command_line,
|
||||
&mut cursor_pos,
|
||||
false,
|
||||
/*is_unique=*/ false, // don't care
|
||||
),
|
||||
};
|
||||
|
||||
@@ -4414,6 +4440,7 @@ fn get_autosuggestion_performer(
|
||||
&search_string,
|
||||
&mut cursor,
|
||||
/*append_only=*/ true,
|
||||
/*is_unique=*/ false,
|
||||
);
|
||||
}
|
||||
result
|
||||
@@ -4820,7 +4847,9 @@ fn fill_history_pager(
|
||||
if search_term != zelf.pager.search_field_line.text() {
|
||||
return; // Stale request.
|
||||
}
|
||||
let history_pager = zelf.history_pager.as_mut().unwrap();
|
||||
let Some(history_pager) = zelf.history_pager.as_mut() else {
|
||||
return; // Pager has been closed.
|
||||
};
|
||||
history_pager.direction = direction;
|
||||
match direction {
|
||||
SearchDirection::Forward => {
|
||||
@@ -4889,6 +4918,10 @@ fn expand_replacer(
|
||||
|new_value| std::mem::replace(&mut parser.libdata_mut().is_interactive, new_value),
|
||||
false,
|
||||
);
|
||||
let _readonly_commandline = scoped_push_replacer(
|
||||
|new_value| std::mem::replace(&mut parser.libdata_mut().readonly_commandline, new_value),
|
||||
true,
|
||||
);
|
||||
|
||||
let mut outputs = vec![];
|
||||
let ret = exec_subshell(
|
||||
@@ -5597,6 +5630,7 @@ pub fn completion_apply_to_command_line(
|
||||
command_line: &wstr,
|
||||
inout_cursor_pos: &mut usize,
|
||||
append_only: bool,
|
||||
is_unique: bool,
|
||||
) -> WString {
|
||||
let add_space = !flags.contains(CompleteFlags::NO_SPACE);
|
||||
let do_replace_token = flags.contains(CompleteFlags::REPLACES_TOKEN);
|
||||
@@ -5621,7 +5655,7 @@ pub fn completion_apply_to_command_line(
|
||||
}
|
||||
|
||||
let mut escape_flags = EscapeFlags::empty();
|
||||
if append_only || !add_space {
|
||||
if append_only || !is_unique || !add_space {
|
||||
escape_flags.insert(EscapeFlags::NO_QUOTED);
|
||||
}
|
||||
if no_tilde {
|
||||
@@ -5791,7 +5825,7 @@ fn compute_and_apply_completions(&mut self, c: ReadlineCmd) {
|
||||
token_range.end += cmdsub_range.start;
|
||||
|
||||
// Wildcard expansion and completion below check for cancellation.
|
||||
terminal_protocols_disable_ifn();
|
||||
terminal_protocols_disable_ifn(false);
|
||||
|
||||
// Check if we have a wildcard within this string; if so we first attempt to expand the
|
||||
// wildcard; if that succeeds we don't then apply user completions (#8593).
|
||||
@@ -5869,7 +5903,12 @@ fn try_insert(&mut self, c: Completion, tok: &wstr, token_range: Range<usize>) {
|
||||
// If this is a replacement completion, check that we know how to replace it, e.g. that
|
||||
// the token doesn't contain evil operators like {}.
|
||||
if !c.flags.contains(CompleteFlags::REPLACES_TOKEN) || reader_can_replace(tok, c.flags) {
|
||||
self.completion_insert(&c.completion, token_range.end, c.flags);
|
||||
self.completion_insert(
|
||||
&c.completion,
|
||||
token_range.end,
|
||||
c.flags,
|
||||
/*is_unique=*/ true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6005,7 +6044,12 @@ fn handle_completions(&mut self, token_range: Range<usize>) -> bool {
|
||||
if prefix_is_partial_completion {
|
||||
flags |= CompleteFlags::NO_SPACE;
|
||||
}
|
||||
self.completion_insert(&common_prefix, token_range.end, flags);
|
||||
self.completion_insert(
|
||||
&common_prefix,
|
||||
token_range.end,
|
||||
flags,
|
||||
/*is_unique=*/ false,
|
||||
);
|
||||
self.cycle_command_line = self.command_line.text().to_owned();
|
||||
self.cycle_cursor_pos = self.command_line.position();
|
||||
}
|
||||
@@ -6049,7 +6093,13 @@ fn handle_completions(&mut self, token_range: Range<usize>) -> bool {
|
||||
/// \param token_end the position after the token to complete
|
||||
/// \param flags A union of all flags describing the completion to insert. See the completion_t
|
||||
/// struct for more information on possible values.
|
||||
fn completion_insert(&mut self, val: &wstr, token_end: usize, flags: CompleteFlags) {
|
||||
fn completion_insert(
|
||||
&mut self,
|
||||
val: &wstr,
|
||||
token_end: usize,
|
||||
flags: CompleteFlags,
|
||||
is_unique: bool,
|
||||
) {
|
||||
let (elt, el) = self.active_edit_line();
|
||||
|
||||
// Move the cursor to the end of the token.
|
||||
@@ -6065,6 +6115,7 @@ fn completion_insert(&mut self, val: &wstr, token_end: usize, flags: CompleteFla
|
||||
el.text(),
|
||||
&mut cursor,
|
||||
/*append_only=*/ false,
|
||||
is_unique,
|
||||
);
|
||||
self.set_buffer_maintaining_pager(&new_command_line, cursor, false);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//! The current implementation is less smart than ncurses allows and can not for example move blocks
|
||||
//! of text around to handle text insertion.
|
||||
|
||||
use crate::future_feature_flags::{feature_test, FeatureFlag};
|
||||
use crate::pager::{PageRendering, Pager, PAGER_MIN_HEIGHT};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::LinkedList;
|
||||
@@ -931,8 +932,11 @@ fn update(
|
||||
} else if left_prompt != zelf.actual_left_prompt || (zelf.scrolled && is_final_rendering) {
|
||||
zelf.r#move(0, 0);
|
||||
let mut start = 0;
|
||||
let osc_133_prompt_start =
|
||||
|zelf: &mut Screen| zelf.write_bytes(b"\x1b]133;A;special_key=1\x07");
|
||||
let osc_133_prompt_start = |zelf: &mut Screen| {
|
||||
if feature_test(FeatureFlag::mark_prompt) {
|
||||
zelf.write_bytes(b"\x1b]133;A;special_key=1\x07");
|
||||
}
|
||||
};
|
||||
if left_prompt_layout.line_breaks.is_empty() {
|
||||
osc_133_prompt_start(&mut zelf);
|
||||
}
|
||||
@@ -1636,7 +1640,11 @@ fn measure_run_from(
|
||||
width = next_tab_stop(width);
|
||||
} else {
|
||||
// Ordinary char. Add its width with care to ignore control chars which have width -1.
|
||||
width += usize::try_from(fish_wcwidth_visible(input.char_at(idx))).unwrap();
|
||||
if let Ok(ww) = usize::try_from(fish_wcwidth_visible(input.char_at(idx))) {
|
||||
width += ww;
|
||||
} else {
|
||||
width = width.saturating_sub(1);
|
||||
}
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
@@ -1682,7 +1690,8 @@ fn truncate_run(
|
||||
curr_width = measure_run_from(run, 0, None, cache);
|
||||
idx = 0;
|
||||
} else {
|
||||
let char_width = usize::try_from(fish_wcwidth_visible(c)).unwrap();
|
||||
// FIXME: In case of backspace, this would remove the last width.
|
||||
let char_width = usize::try_from(fish_wcwidth_visible(c)).unwrap_or(0);
|
||||
curr_width -= std::cmp::min(curr_width, char_width);
|
||||
run.remove(idx);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,8 @@ macro_rules! unique_completion_applies_as {
|
||||
completions[0].flags,
|
||||
cmdline,
|
||||
&mut cursor,
|
||||
false,
|
||||
/*append_only=*/ false,
|
||||
/*is_unique=*/ true,
|
||||
);
|
||||
assert_eq!(newcmdline, L!($applied), "apply result mismatch");
|
||||
};
|
||||
@@ -224,6 +225,7 @@ macro_rules! unique_completion_applies_as {
|
||||
L!("mv debug debug"),
|
||||
&mut cursor_pos,
|
||||
true,
|
||||
/*is_unique=*/ false,
|
||||
);
|
||||
assert_eq!(newcmdline, L!("mv debug Debug/"));
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::env::EnvStack;
|
||||
use crate::input::{EventQueuePeeker, InputMappingSet, KeyNameStyle, DEFAULT_BIND_MODE};
|
||||
use crate::input_common::{CharEvent, InputData, InputEventQueuer};
|
||||
use crate::input_common::{CharEvent, InputData, InputEventQueuer, KeyEvent};
|
||||
use crate::key::Key;
|
||||
use crate::wchar::prelude::*;
|
||||
use std::rc::Rc;
|
||||
@@ -52,8 +52,10 @@ fn test_input() {
|
||||
);
|
||||
|
||||
// Push the desired binding to the queue.
|
||||
for c in desired_binding {
|
||||
input.input_data.queue_char(CharEvent::from_key(c));
|
||||
for key in desired_binding {
|
||||
input
|
||||
.input_data
|
||||
.queue_char(CharEvent::from_key(KeyEvent::from(key)));
|
||||
}
|
||||
|
||||
let mut peeker = EventQueuePeeker::new(&mut input);
|
||||
|
||||
@@ -439,5 +439,10 @@ macro_rules! validate {
|
||||
0, r#"echo "$()"'"#,
|
||||
0, "\n"
|
||||
);
|
||||
validate!(
|
||||
0, r#"""#,
|
||||
0, "\n",
|
||||
0, r#"$()"$() ""#
|
||||
);
|
||||
})();
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ macro_rules! validate {
|
||||
&line,
|
||||
&mut cursor_pos,
|
||||
$append_only,
|
||||
/*is_unique=*/ false,
|
||||
);
|
||||
assert_eq!(result, expected);
|
||||
assert_eq!(cursor_pos, out_cursor_pos);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user