mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-30 19:41:15 -03:00
Compare commits
177 Commits
4.1.2
...
fish-reena
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c7062def9 | ||
|
|
8a4a8bb6a0 | ||
|
|
e1b064f6cc | ||
|
|
68a8cd4501 | ||
|
|
5f0d83d2f2 | ||
|
|
03f54171c6 | ||
|
|
f391b4a179 | ||
|
|
8db674b6b5 | ||
|
|
f03113d048 | ||
|
|
6c48e214ca | ||
|
|
189a2e90dd | ||
|
|
9b44138917 | ||
|
|
ec7d20b347 | ||
|
|
84b52a3ed1 | ||
|
|
30b1c9570f | ||
|
|
b88622bc35 | ||
|
|
a4edb4020d | ||
|
|
e1e5dfdd62 | ||
|
|
5102c8b137 | ||
|
|
9ae9db7f70 | ||
|
|
d0aaa8d809 | ||
|
|
6024539c12 | ||
|
|
fb06ad4a44 | ||
|
|
598e98794c | ||
|
|
b3a295959d | ||
|
|
50dfd962ec | ||
|
|
65332eaacc | ||
|
|
a00e6f8696 | ||
|
|
6415dfbd35 | ||
|
|
8c387c58de | ||
|
|
da411f6fa7 | ||
|
|
8fe402e9f7 | ||
|
|
c41fc52077 | ||
|
|
f7d730390c | ||
|
|
8fc30d5243 | ||
|
|
93c4d63295 | ||
|
|
7a07c08860 | ||
|
|
1cf110d083 | ||
|
|
fef358fc74 | ||
|
|
b5feb79a7c | ||
|
|
4d52245617 | ||
|
|
ff308b36af | ||
|
|
fa8cf8a1a5 | ||
|
|
5ade4a037e | ||
|
|
861002917a | ||
|
|
eddb26d490 | ||
|
|
b7fe3190bb | ||
|
|
b6ddb56cc7 | ||
|
|
8dd59081d7 | ||
|
|
3ae17ea100 | ||
|
|
10c34c5353 | ||
|
|
e3ebda3647 | ||
|
|
092e7fa274 | ||
|
|
dd47c2baa2 | ||
|
|
15065255e9 | ||
|
|
594f1df39c | ||
|
|
724416125e | ||
|
|
3afafe6398 | ||
|
|
af7446a055 | ||
|
|
7be101e8c9 | ||
|
|
5f18b173dd | ||
|
|
3fec9c8145 | ||
|
|
bdca70bfb0 | ||
|
|
5e28f068ec | ||
|
|
a0b22077a5 | ||
|
|
1d36b04ea6 | ||
|
|
6829c9d678 | ||
|
|
e1f6ab8916 | ||
|
|
778baaecb5 | ||
|
|
a189f79590 | ||
|
|
6395644e8c | ||
|
|
a958f23f63 | ||
|
|
ec8756d7a3 | ||
|
|
b7fabb11ac | ||
|
|
74ba4e9a98 | ||
|
|
b1e8fdfaa2 | ||
|
|
9eb439c01d | ||
|
|
fbfd29d6d2 | ||
|
|
f158a3ae3e | ||
|
|
7d59b4f4e2 | ||
|
|
e99eca47c3 | ||
|
|
d683769e1f | ||
|
|
9ea328e43a | ||
|
|
f6d93f2fdb | ||
|
|
b644fdbb04 | ||
|
|
7647d68b68 | ||
|
|
d167ab9376 | ||
|
|
e68bd2f980 | ||
|
|
b5c17d4743 | ||
|
|
66ca7ac6d0 | ||
|
|
e97a616ffa | ||
|
|
061517cd14 | ||
|
|
6accc475c9 | ||
|
|
c2e2fd6432 | ||
|
|
83af5c91bd | ||
|
|
e9f5982147 | ||
|
|
50819666b1 | ||
|
|
6ad13e35c0 | ||
|
|
39e2f1138b | ||
|
|
cd37c71e29 | ||
|
|
c1f3d93b3b | ||
|
|
0aa05032c4 | ||
|
|
174130fe2f | ||
|
|
d06f7f01d2 | ||
|
|
a04ddd9b17 | ||
|
|
12929fed74 | ||
|
|
87bf580f68 | ||
|
|
66bab5e767 | ||
|
|
4b12fb2887 | ||
|
|
623c14aed0 | ||
|
|
7d83dc4758 | ||
|
|
493d0bca95 | ||
|
|
983501ff8c | ||
|
|
20da9a2b51 | ||
|
|
7aec6c55f9 | ||
|
|
532f30e031 | ||
|
|
1d7ab57e3a | ||
|
|
8adc598e90 | ||
|
|
c884c08257 | ||
|
|
66dc734c11 | ||
|
|
77fee9acb9 | ||
|
|
6b66c2bc1d | ||
|
|
81b9f50dc2 | ||
|
|
fcd246064b | ||
|
|
86a0a348ee | ||
|
|
ed36e852d2 | ||
|
|
da5d93c1e2 | ||
|
|
7b59ae0d82 | ||
|
|
97acc12d62 | ||
|
|
db6a7d26cd | ||
|
|
6be03d7cc4 | ||
|
|
617a6edb13 | ||
|
|
31c85723e8 | ||
|
|
d22c905d9f | ||
|
|
216dc2d473 | ||
|
|
918e7abe6b | ||
|
|
c145ee6df3 | ||
|
|
62543b36a4 | ||
|
|
751aad5302 | ||
|
|
efabab492a | ||
|
|
c7cdbe60cd | ||
|
|
412149a5de | ||
|
|
abd23d2a1b | ||
|
|
b774c54a6f | ||
|
|
e4b797405b | ||
|
|
81a89a5dec | ||
|
|
0da12a6b55 | ||
|
|
86ec8994e6 | ||
|
|
caf426ddb2 | ||
|
|
508ae410a6 | ||
|
|
993b977c9b | ||
|
|
a7f0138fc7 | ||
|
|
ab3c932903 | ||
|
|
ae0fdadcff | ||
|
|
e3974989d8 | ||
|
|
080b1e0e4f | ||
|
|
a5db91dd85 | ||
|
|
b62f54631b | ||
|
|
d835c5252a | ||
|
|
a53db72564 | ||
|
|
61b0368dac | ||
|
|
568b4a22f9 | ||
|
|
8abba8a089 | ||
|
|
b3b789cd68 | ||
|
|
425a166111 | ||
|
|
1dcc290e29 | ||
|
|
863204dbfa | ||
|
|
4b21e7c9c7 | ||
|
|
df5230ff4a | ||
|
|
7cd0943056 | ||
|
|
6f0532460a | ||
|
|
29a35a7951 | ||
|
|
dd0d45f88f | ||
|
|
0ff0de7efe | ||
|
|
092ef99551 | ||
|
|
97ae05b69d | ||
|
|
3d8eca178e |
@@ -1,7 +1,6 @@
|
|||||||
image: alpine/edge
|
image: alpine/edge
|
||||||
packages:
|
packages:
|
||||||
- cargo
|
- cargo
|
||||||
- clang17-libclang
|
|
||||||
- cmake
|
- cmake
|
||||||
- ninja
|
- ninja
|
||||||
- pcre2-dev
|
- pcre2-dev
|
||||||
@@ -24,4 +23,4 @@ tasks:
|
|||||||
ninja
|
ninja
|
||||||
- test: |
|
- test: |
|
||||||
cd fish-shell/build
|
cd fish-shell/build
|
||||||
ninja test
|
ninja fish_run_tests
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ tasks:
|
|||||||
ninja
|
ninja
|
||||||
- test: |
|
- test: |
|
||||||
cd fish/build
|
cd fish/build
|
||||||
ninja test
|
ninja fish_run_tests
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ packages:
|
|||||||
- gettext
|
- gettext
|
||||||
- gmake
|
- gmake
|
||||||
- llvm
|
- llvm
|
||||||
- terminfo-db
|
|
||||||
- ninja
|
- ninja
|
||||||
- pcre2
|
- pcre2
|
||||||
- py311-pexpect
|
- py311-pexpect
|
||||||
@@ -27,4 +26,4 @@ tasks:
|
|||||||
ninja
|
ninja
|
||||||
- test: |
|
- test: |
|
||||||
cd fish-shell/build
|
cd fish-shell/build
|
||||||
ninja test
|
ninja fish_run_tests
|
||||||
|
|||||||
29
.builds/openbsd.yml
Normal file
29
.builds/openbsd.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
image: openbsd/latest
|
||||||
|
packages:
|
||||||
|
- cmake
|
||||||
|
- gcc
|
||||||
|
- gettext
|
||||||
|
- gmake
|
||||||
|
- llvm
|
||||||
|
- ninja
|
||||||
|
- pcre2
|
||||||
|
- py311-pexpect
|
||||||
|
- python
|
||||||
|
- rust
|
||||||
|
- tmux
|
||||||
|
sources:
|
||||||
|
- https://github.com/fish-shell/fish-shell
|
||||||
|
tasks:
|
||||||
|
- build: |
|
||||||
|
cd fish-shell
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -GNinja .. \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DCMAKE_INSTALL_DATADIR=share \
|
||||||
|
-DCMAKE_INSTALL_DOCDIR=share/doc/fish \
|
||||||
|
-DCMAKE_INSTALL_SYSCONFDIR=/etc
|
||||||
|
ninja
|
||||||
|
- test: |
|
||||||
|
cd fish-shell/build
|
||||||
|
ninja fish_run_tests
|
||||||
21
.cirrus.yml
21
.cirrus.yml
@@ -1,3 +1,5 @@
|
|||||||
|
skip: $CIRRUS_REPO_OWNER == 'fish-shell' && $CIRRUS_BRANCH == 'master'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CIRRUS_CLONE_DEPTH: 100
|
CIRRUS_CLONE_DEPTH: 100
|
||||||
CI: 1
|
CI: 1
|
||||||
@@ -6,20 +8,12 @@ linux_task:
|
|||||||
matrix:
|
matrix:
|
||||||
- name: alpine
|
- name: alpine
|
||||||
container: &step
|
container: &step
|
||||||
image: ghcr.io/krobelus/fish-ci/alpine:latest
|
image: ghcr.io/fish-shell/fish-ci/alpine:latest
|
||||||
memory: 4GB
|
memory: 4GB
|
||||||
- name: jammy
|
- name: jammy
|
||||||
container:
|
container:
|
||||||
<<: *step
|
<<: *step
|
||||||
image: ghcr.io/krobelus/fish-ci/jammy:latest
|
image: ghcr.io/fish-shell/fish-ci/jammy:latest
|
||||||
# - name: jammy-asan
|
|
||||||
# container:
|
|
||||||
# <<: *step
|
|
||||||
# image: ghcr.io/krobelus/fish-ci/jammy-asan:latest
|
|
||||||
# - name: focal-32bit
|
|
||||||
# container:
|
|
||||||
# <<: *step
|
|
||||||
# image: ghcr.io/krobelus/fish-ci/focal-32bit:latest
|
|
||||||
tests_script:
|
tests_script:
|
||||||
# cirrus at times gives us 32 procs and 2 GB of RAM
|
# cirrus at times gives us 32 procs and 2 GB of RAM
|
||||||
# Unrestriced parallelism results in OOM
|
# Unrestriced parallelism results in OOM
|
||||||
@@ -36,9 +30,6 @@ linux_arm_task:
|
|||||||
- name: focal-arm64
|
- name: focal-arm64
|
||||||
arm_container:
|
arm_container:
|
||||||
image: ghcr.io/fish-shell/fish-ci/focal-arm64
|
image: ghcr.io/fish-shell/fish-ci/focal-arm64
|
||||||
- name: jammy-armv7-32bit
|
|
||||||
arm_container:
|
|
||||||
image: ghcr.io/fish-shell/fish-ci/jammy-armv7-32bit
|
|
||||||
tests_script:
|
tests_script:
|
||||||
# cirrus at times gives us 32 procs and 2 GB of RAM
|
# cirrus at times gives us 32 procs and 2 GB of RAM
|
||||||
# Unrestriced parallelism results in OOM
|
# Unrestriced parallelism results in OOM
|
||||||
@@ -47,10 +38,8 @@ linux_arm_task:
|
|||||||
- mkdir build && cd build
|
- mkdir build && cd build
|
||||||
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
- ninja -j 6 fish
|
- ninja -j 6 fish
|
||||||
- file ./fish
|
|
||||||
- ninja fish_run_tests
|
- ninja fish_run_tests
|
||||||
# CI task disabled during RIIR transition
|
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||||
only_if: false && $CIRRUS_REPO_OWNER == 'fish-shell'
|
|
||||||
|
|
||||||
freebsd_task:
|
freebsd_task:
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
43
.github/actions/install-dependencies/action.yml
vendored
Normal file
43
.github/actions/install-dependencies/action.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: Install dependencies for system tests
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
include_sphinx:
|
||||||
|
description: Whether to install Sphinx
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
include_pcre:
|
||||||
|
description: Whether to install the PCRE library
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- shell: bash
|
||||||
|
env:
|
||||||
|
include_sphinx: ${{ inputs.include_sphinx }}
|
||||||
|
include_pcre: ${{ inputs.include_pcre }}
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
: "optional dependencies"
|
||||||
|
sudo apt install \
|
||||||
|
gettext \
|
||||||
|
$(if $include_pcre; then echo libpcre2-dev; fi) \
|
||||||
|
$(if $include_sphinx; then echo python3-sphinx; fi) \
|
||||||
|
;
|
||||||
|
: "system test dependencies"
|
||||||
|
sudo apt install \
|
||||||
|
diffutils $(: "for diff") \
|
||||||
|
git \
|
||||||
|
gettext \
|
||||||
|
less \
|
||||||
|
$(if ${{ inputs.include_pcre }}; then echo libpcre2-dev; fi) \
|
||||||
|
python3-pexpect \
|
||||||
|
tmux \
|
||||||
|
wget \
|
||||||
|
;
|
||||||
|
- uses: ./.github/actions/install-sphinx-markdown-builder
|
||||||
|
if: ${{ inputs.include_sphinx == 'true' }}
|
||||||
41
.github/actions/rust-toolchain/action.yml
vendored
Normal file
41
.github/actions/rust-toolchain/action.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Rust Toolchain
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
toolchain_channel:
|
||||||
|
description: Either "stable" or "msrv"
|
||||||
|
required: true
|
||||||
|
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:
|
||||||
|
- name: Set toolchain
|
||||||
|
env:
|
||||||
|
toolchain_channel: ${{ inputs.toolchain_channel }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
toolchain=$(
|
||||||
|
case "$toolchain_channel" in
|
||||||
|
(stable) echo 1.90 ;;
|
||||||
|
(msrv) echo 1.70 ;;
|
||||||
|
(*)
|
||||||
|
printf >&2 "error: unsupported toolchain channel %s" "$toolchain_channel"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
)
|
||||||
|
printf 'TOOLCHAIN=%s\n' "$toolchain" >>"$GITHUB_ENV"
|
||||||
|
- uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: ${{ env.TOOLCHAIN }}
|
||||||
|
targets: ${{ inputs.targets }}
|
||||||
|
components: ${{ inputs.components }}
|
||||||
@@ -14,7 +14,8 @@ permissions:
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- uses: dtolnay/rust-toolchain@1.70
|
- uses: ./.github/actions/rust-toolchain
|
||||||
with:
|
with:
|
||||||
|
toolchain_channel: "msrv"
|
||||||
targets: ${{ inputs.targets }}
|
targets: ${{ inputs.targets }}
|
||||||
components: ${{ inputs.components}}
|
components: ${{ inputs.components }}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ permissions:
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- uses: dtolnay/rust-toolchain@1.90
|
- uses: ./.github/actions/rust-toolchain
|
||||||
with:
|
with:
|
||||||
|
toolchain_channel: "stable"
|
||||||
targets: ${{ inputs.targets }}
|
targets: ${{ inputs.targets }}
|
||||||
components: ${{ inputs.components }}
|
components: ${{ inputs.components }}
|
||||||
|
|||||||
126
.github/workflows/build_docker_images.yml
vendored
Normal file
126
.github/workflows/build_docker_images.yml
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
name: Build Docker test images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: docker-builds
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
NAMESPACE: fish-ci
|
||||||
|
ONLY_FOR_REPO_OWNER: fish-shell
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-docker-changes:
|
||||||
|
if: github.repository_owner == env.ONLY_FOR_REPO_OWNER
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
docker-changed: ${{ steps.changes.outputs.docker }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: dorny/paths-filter@v3
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
docker:
|
||||||
|
- 'docker/**'
|
||||||
|
|
||||||
|
docker-build:
|
||||||
|
needs: check-docker-changes
|
||||||
|
if: github.repository_owner == env.ONLY_FOR_REPO_OWNER && needs.check-docker-changes.outputs.docker-changed == 'true'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: alpine
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: centos9
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: fedora
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: focal-32bit
|
||||||
|
- os: ubuntu-24.04-arm
|
||||||
|
target: focal-arm64
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: focal
|
||||||
|
- os: ubuntu-24.04-arm
|
||||||
|
target: jammy-armv7-32bit
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: jammy-asan
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: jammy-tsan
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: jammy
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: noble
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: opensuse-tumbleweed
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
-
|
||||||
|
name: Login to Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
-
|
||||||
|
name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.NAMESPACE }}/${{ matrix.target }}
|
||||||
|
flavor: |
|
||||||
|
latest=true
|
||||||
|
-
|
||||||
|
name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: docker/context
|
||||||
|
push: true
|
||||||
|
file: docker/${{ matrix.target }}.Dockerfile
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|
||||||
|
trigger-cirrus:
|
||||||
|
needs: [check-docker-changes, docker-build]
|
||||||
|
if: always() && github.repository_owner == env.ONLY_FOR_REPO_OWNER
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Trigger Cirrus CI
|
||||||
|
env:
|
||||||
|
CIRRUS_TOKEN: ${{ secrets.CIRRUS_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
# N.B. push-triggered workflows are usually from master.
|
||||||
|
branch=${{ github.ref_name }}
|
||||||
|
repository_id=${{ github.repository_id }}
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: Bearer $CIRRUS_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"query": "mutation {
|
||||||
|
createBuild(input: {
|
||||||
|
repositoryId: \"$repository_id\",
|
||||||
|
branch: \"$branch\"
|
||||||
|
})
|
||||||
|
{ build { id } }
|
||||||
|
}"
|
||||||
|
}' \
|
||||||
|
https://api.cirrus-ci.com/graphql
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Rust checks
|
name: Lint
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
@@ -6,25 +6,36 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
rustfmt:
|
format:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/rust-toolchain@stable
|
- uses: ./.github/actions/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- name: cargo fmt
|
- name: install dependencies
|
||||||
run: cargo fmt --check
|
run: pip install ruff
|
||||||
|
- name: build fish
|
||||||
|
run: cargo build
|
||||||
|
- name: check format
|
||||||
|
run: PATH="target/debug:$PATH" build_tools/style.fish --all --check
|
||||||
|
|
||||||
clippy-stable:
|
clippy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
features: ["", "--no-default-features"]
|
include:
|
||||||
|
- rust_version: "stable"
|
||||||
|
features: ""
|
||||||
|
- rust_version: "stable"
|
||||||
|
features: "--no-default-features"
|
||||||
|
- rust_version: "msrv"
|
||||||
|
features: ""
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/actions/rust-toolchain@stable
|
- uses: ./.github/actions/rust-toolchain
|
||||||
with:
|
with:
|
||||||
|
toolchain_channel: ${{ matrix.rust_version }}
|
||||||
components: clippy
|
components: clippy
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
@@ -32,19 +43,6 @@ jobs:
|
|||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
run: cargo clippy --workspace --all-targets ${{ matrix.features }} -- --deny=warnings
|
run: cargo clippy --workspace --all-targets ${{ matrix.features }} -- --deny=warnings
|
||||||
|
|
||||||
clippy-msrv:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
|
||||||
with:
|
|
||||||
components: clippy
|
|
||||||
- name: Install deps
|
|
||||||
run: |
|
|
||||||
sudo apt install gettext
|
|
||||||
- name: cargo clippy
|
|
||||||
run: cargo clippy --workspace --all-targets -- --deny=warnings
|
|
||||||
|
|
||||||
rustdoc:
|
rustdoc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
1
.github/workflows/lockthreads.yml
vendored
1
.github/workflows/lockthreads.yml
vendored
@@ -12,6 +12,7 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lock:
|
lock:
|
||||||
|
if: github.repository_owner == 'fish-shell'
|
||||||
permissions:
|
permissions:
|
||||||
issues: write # for dessant/lock-threads to lock issues
|
issues: write # for dessant/lock-threads to lock issues
|
||||||
pull-requests: write # for dessant/lock-threads to lock PRs
|
pull-requests: write # for dessant/lock-threads to lock PRs
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -150,6 +150,8 @@ jobs:
|
|||||||
uses: ./.github/actions/rust-toolchain@stable
|
uses: ./.github/actions/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: aarch64-apple-darwin
|
targets: aarch64-apple-darwin
|
||||||
|
- name: Install dependencies
|
||||||
|
run: brew install gettext
|
||||||
- name: Build and codesign
|
- name: Build and codesign
|
||||||
run: |
|
run: |
|
||||||
die() { echo >&2 "$*"; exit 1; }
|
die() { echo >&2 "$*"; exit 1; }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: make fish_run_tests
|
name: Test
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
@@ -11,18 +11,17 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ubuntu:
|
ubuntu:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
|
uses: ./.github/actions/install-dependencies
|
||||||
|
with:
|
||||||
|
include_sphinx: true
|
||||||
|
- name: Generate a locale that uses a comma as decimal separator.
|
||||||
run: |
|
run: |
|
||||||
sudo apt install gettext libpcre2-dev python3-pexpect python3-sphinx tmux
|
|
||||||
# Generate a locale that uses a comma as decimal separator.
|
|
||||||
sudo locale-gen fr_FR.UTF-8
|
sudo locale-gen fr_FR.UTF-8
|
||||||
- uses: ./.github/actions/install-sphinx-markdown-builder
|
|
||||||
- name: cmake
|
- name: cmake
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@@ -43,18 +42,20 @@ jobs:
|
|||||||
git --no-pager diff --exit-code || { echo 'There are uncommitted changes after regenerating the gettext PO files. Make sure to update them via `build_tools/update_translations.fish` after changing source files.'; exit 1; }
|
git --no-pager diff --exit-code || { echo 'There are uncommitted changes after regenerating the gettext PO files. Make sure to update them via `build_tools/update_translations.fish` after changing source files.'; exit 1; }
|
||||||
|
|
||||||
ubuntu-32bit-static-pcre2:
|
ubuntu-32bit-static-pcre2:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
- uses: ./.github/actions/rust-toolchain@oldest-supported
|
||||||
with:
|
with:
|
||||||
targets: "i586-unknown-linux-gnu" # rust-toolchain wants this comma-separated
|
targets: "i586-unknown-linux-gnu"
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
|
uses: ./.github/actions/install-dependencies
|
||||||
|
with:
|
||||||
|
include_pcre: false
|
||||||
|
include_sphinx: false
|
||||||
|
- name: Install g++-multilib
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt install g++-multilib
|
||||||
sudo apt install gettext python3-pexpect g++-multilib tmux
|
|
||||||
- name: cmake
|
- name: cmake
|
||||||
env:
|
env:
|
||||||
CFLAGS: "-m32"
|
CFLAGS: "-m32"
|
||||||
@@ -69,7 +70,6 @@ jobs:
|
|||||||
make -C build VERBOSE=1 fish_run_tests
|
make -C build VERBOSE=1 fish_run_tests
|
||||||
|
|
||||||
ubuntu-asan:
|
ubuntu-asan:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
# Rust has two different memory sanitizers of interest; they can't be used at the same time:
|
# Rust has two different memory sanitizers of interest; they can't be used at the same time:
|
||||||
@@ -79,7 +79,6 @@ jobs:
|
|||||||
#
|
#
|
||||||
RUSTFLAGS: "-Zsanitizer=address"
|
RUSTFLAGS: "-Zsanitizer=address"
|
||||||
# RUSTFLAGS: "-Zsanitizer=memory -Zsanitizer-memory-track-origins"
|
# RUSTFLAGS: "-Zsanitizer=memory -Zsanitizer-memory-track-origins"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
# All -Z options require running nightly
|
# All -Z options require running nightly
|
||||||
@@ -89,8 +88,11 @@ jobs:
|
|||||||
# this is comma-separated
|
# this is comma-separated
|
||||||
components: rust-src
|
components: rust-src
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
|
uses: ./.github/actions/install-dependencies
|
||||||
|
with:
|
||||||
|
include_sphinx: false
|
||||||
|
- name: Install llvm
|
||||||
run: |
|
run: |
|
||||||
sudo apt install gettext libpcre2-dev python3-pexpect tmux
|
|
||||||
sudo apt install llvm # for llvm-symbolizer
|
sudo apt install llvm # for llvm-symbolizer
|
||||||
- name: cmake
|
- name: cmake
|
||||||
env:
|
env:
|
||||||
@@ -119,38 +121,8 @@ jobs:
|
|||||||
export LSAN_OPTIONS="$LSAN_OPTIONS:suppressions=$PWD/build_tools/lsan_suppressions.txt"
|
export LSAN_OPTIONS="$LSAN_OPTIONS:suppressions=$PWD/build_tools/lsan_suppressions.txt"
|
||||||
make -C build VERBOSE=1 fish_run_tests
|
make -C build VERBOSE=1 fish_run_tests
|
||||||
|
|
||||||
# Our clang++ tsan builds are not recognizing safe rust patterns (such as the fact that Drop
|
|
||||||
# cannot be called while a thread is using the object in question). Rust has its own way of
|
|
||||||
# running TSAN, but for the duration of the port from C++ to Rust, we'll keep this disabled.
|
|
||||||
|
|
||||||
# ubuntu-threadsan:
|
|
||||||
#
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
#
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v4
|
|
||||||
# - uses: ./.github/actions/rust-toolchain@oldest-supported
|
|
||||||
# - name: Install deps
|
|
||||||
# run: |
|
|
||||||
# sudo apt install gettext libpcre2-dev python3-pexpect tmux
|
|
||||||
# - name: cmake
|
|
||||||
# env:
|
|
||||||
# FISH_CI_SAN: 1
|
|
||||||
# CC: clang
|
|
||||||
# run: |
|
|
||||||
# mkdir build && cd build
|
|
||||||
# cmake ..
|
|
||||||
# - name: make
|
|
||||||
# run: |
|
|
||||||
# make
|
|
||||||
# - name: make fish_run_tests
|
|
||||||
# run: |
|
|
||||||
# make -C build fish_run_tests
|
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
|
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# macOS runners keep having issues loading Cargo.toml dependencies from git (GitHub) instead
|
# 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.
|
# of crates.io, so give this a try. It's also sometimes significantly faster on all platforms.
|
||||||
@@ -163,7 +135,7 @@ jobs:
|
|||||||
# --break-system-packages because homebrew has now declared itself "externally managed".
|
# --break-system-packages because homebrew has now declared itself "externally managed".
|
||||||
# this is CI so we don't actually care.
|
# this is CI so we don't actually care.
|
||||||
sudo pip3 install --break-system-packages pexpect
|
sudo pip3 install --break-system-packages pexpect
|
||||||
brew install tmux
|
brew install gettext tmux
|
||||||
- name: cmake
|
- name: cmake
|
||||||
run: |
|
run: |
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
@@ -174,3 +146,28 @@ jobs:
|
|||||||
- name: make fish_run_tests
|
- name: make fish_run_tests
|
||||||
run: |
|
run: |
|
||||||
make -C build VERBOSE=1 fish_run_tests
|
make -C build VERBOSE=1 fish_run_tests
|
||||||
|
|
||||||
|
windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
update: true
|
||||||
|
msystem: MSYS
|
||||||
|
- name: Install deps
|
||||||
|
# Not using setup-msys2 `install` option to make it easier to copy/paste
|
||||||
|
run: |
|
||||||
|
pacman --noconfirm -S --needed git rust
|
||||||
|
- name: cargo build
|
||||||
|
run: |
|
||||||
|
cargo build
|
||||||
|
- name: smoketest
|
||||||
|
# We can't use `cargo test` yet, there are just too many failures
|
||||||
|
# so this is just a quick check to make sure that fish can swim
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
[ "$(target/debug/fish.exe -c 'echo (math 1 + 1)')" = 2 ]
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -105,3 +105,6 @@ target/
|
|||||||
|
|
||||||
# JetBrains editors.
|
# JetBrains editors.
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# AI slop
|
||||||
|
.claude/
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
fish 4.1.2 (released October 07, 2025)
|
fish ?.?.? (released ???)
|
||||||
======================================
|
=========================
|
||||||
|
|
||||||
|
fish 4.1.3 (released ???)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
This release fixes the following regressions identified in 4.1.0:
|
||||||
|
|
||||||
|
- Crash on invalid :doc:`function <cmds/function>` command (:issue:`11912`).
|
||||||
|
|
||||||
|
as well as the following regressions identified in 4.0.0:
|
||||||
|
|
||||||
|
- Crash when passing negative PIDs to :doc:`wait <cmds/wait>` (:issue:`11929`).
|
||||||
|
|
||||||
|
fish 4.1.2 (released October 7, 2025)
|
||||||
|
=====================================
|
||||||
|
|
||||||
This release fixes the following regressions identified in 4.1.0:
|
This release fixes the following regressions identified in 4.1.0:
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ before committing your change. That will run our autoformatters:
|
|||||||
|
|
||||||
- ``rustfmt`` for Rust
|
- ``rustfmt`` for Rust
|
||||||
- ``fish_indent`` (shipped with fish) for fish script
|
- ``fish_indent`` (shipped with fish) for fish script
|
||||||
- ``black`` for python
|
- ``ruff format`` for python
|
||||||
|
|
||||||
If you’ve already committed your changes that’s okay since it will then
|
If you’ve already committed your changes that’s okay since it will then
|
||||||
check the files in the most recent commit. This can be useful after
|
check the files in the most recent commit. This can be useful after
|
||||||
@@ -343,7 +343,7 @@ command-line and graphical user interface programs. For simple use, you can use
|
|||||||
|
|
||||||
Open up the PO file, for example ``po/sv.po``, and you'll see something like::
|
Open up the PO file, for example ``po/sv.po``, and you'll see something like::
|
||||||
|
|
||||||
msgid "%ls: No suitable job\n"
|
msgid "%s: No suitable job\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
The ``msgid`` here is the "name" of the string to translate, typically the English string to translate.
|
The ``msgid`` here is the "name" of the string to translate, typically the English string to translate.
|
||||||
@@ -351,10 +351,10 @@ The second line (``msgstr``) is where your translation goes.
|
|||||||
|
|
||||||
For example::
|
For example::
|
||||||
|
|
||||||
msgid "%ls: No suitable job\n"
|
msgid "%s: No suitable job\n"
|
||||||
msgstr "%ls: Inget passande jobb\n"
|
msgstr "%s: Inget passande jobb\n"
|
||||||
|
|
||||||
Any ``%s`` / ``%ls`` or ``%d`` are placeholders that fish will use for formatting at runtime. It is important that they match - the translated string should have the same placeholders in the same order.
|
Any ``%s`` or ``%d`` are placeholders that fish will use for formatting at runtime. It is important that they match - the translated string should have the same placeholders in the same order.
|
||||||
|
|
||||||
Also any escaped characters, like that ``\n`` newline at the end, should be kept so the translation has the same behavior.
|
Also any escaped characters, like that ``\n`` newline at the end, should be kept so the translation has the same behavior.
|
||||||
|
|
||||||
@@ -381,7 +381,7 @@ macros:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
streams.out.append(wgettext_fmt!("%ls: There are no jobs\n", argv[0]));
|
streams.out.append(wgettext_fmt!("%s: There are no jobs\n", argv[0]));
|
||||||
|
|
||||||
All messages in fish script must be enclosed in single or double quote
|
All messages in fish script must be enclosed in single or double quote
|
||||||
characters for our message extraction script to find them.
|
characters for our message extraction script to find them.
|
||||||
|
|||||||
30
Cargo.lock
generated
30
Cargo.lock
generated
@@ -42,9 +42,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg_aliases"
|
name = "cfg_aliases"
|
||||||
@@ -105,10 +105,11 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fish"
|
name = "fish"
|
||||||
version = "4.1.2"
|
version = "4.1.0-snapshot"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cc",
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
"errno",
|
"errno",
|
||||||
"fish-build-helper",
|
"fish-build-helper",
|
||||||
"fish-build-man-pages",
|
"fish-build-man-pages",
|
||||||
@@ -118,6 +119,7 @@ dependencies = [
|
|||||||
"fish-printf",
|
"fish-printf",
|
||||||
"libc",
|
"libc",
|
||||||
"lru",
|
"lru",
|
||||||
|
"macro_rules_attribute",
|
||||||
"nix",
|
"nix",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -254,6 +256,22 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "macro_rules_attribute"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520"
|
||||||
|
dependencies = [
|
||||||
|
"macro_rules_attribute-proc_macro",
|
||||||
|
"paste",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "macro_rules_attribute-proc_macro"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
@@ -326,6 +344,12 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pcre2"
|
name = "pcre2"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ repository = "https://github.com/fish-shell/fish-shell"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
bitflags = "2.5.0"
|
bitflags = "2.5.0"
|
||||||
cc = "1.0.94"
|
cc = "1.0.94"
|
||||||
|
cfg-if = "1.0.3"
|
||||||
errno = "0.3.0"
|
errno = "0.3.0"
|
||||||
fish-build-helper = { path = "crates/build-helper" }
|
fish-build-helper = { path = "crates/build-helper" }
|
||||||
fish-build-man-pages = { path = "crates/build-man-pages" }
|
fish-build-man-pages = { path = "crates/build-man-pages" }
|
||||||
@@ -64,7 +65,7 @@ debug = true
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "fish"
|
name = "fish"
|
||||||
version = "4.1.2"
|
version = "4.1.0-snapshot"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
default-run = "fish"
|
default-run = "fish"
|
||||||
@@ -76,6 +77,7 @@ readme = "README.rst"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags.workspace = true
|
bitflags.workspace = true
|
||||||
|
cfg-if.workspace = true
|
||||||
errno.workspace = true
|
errno.workspace = true
|
||||||
fish-build-helper.workspace = true
|
fish-build-helper.workspace = true
|
||||||
fish-build-man-pages = { workspace = true, optional = true }
|
fish-build-man-pages = { workspace = true, optional = true }
|
||||||
@@ -84,6 +86,7 @@ fish-gettext-maps = { workspace = true, optional = true }
|
|||||||
fish-printf.workspace = true
|
fish-printf.workspace = true
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
lru.workspace = true
|
lru.workspace = true
|
||||||
|
macro_rules_attribute = "0.2.2"
|
||||||
nix.workspace = true
|
nix.workspace = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
once_cell.workspace = true
|
once_cell.workspace = true
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ Compiling fish requires:
|
|||||||
Sphinx is also optionally required to build the documentation from a
|
Sphinx is also optionally required to build the documentation from a
|
||||||
cloned git repository.
|
cloned git repository.
|
||||||
|
|
||||||
Additionally, running the full test suite requires Python 3.5+, tmux, and the pexpect package.
|
Additionally, running the full test suite requires diff, git, Python 3.5+, pexpect, less, tmux and wget.
|
||||||
|
|
||||||
Building from source with CMake
|
Building from source with CMake
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|||||||
106
build.rs
106
build.rs
@@ -1,6 +1,6 @@
|
|||||||
#![allow(clippy::uninlined_format_args)]
|
#![allow(clippy::uninlined_format_args)]
|
||||||
|
|
||||||
use fish_build_helper::{fish_build_dir, workspace_root};
|
use fish_build_helper::{env_var, fish_build_dir, workspace_root};
|
||||||
use rsconf::Target;
|
use rsconf::Target;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -30,9 +30,9 @@ fn main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Some build info
|
// Some build info
|
||||||
rsconf::set_env_value("BUILD_TARGET_TRIPLE", &env::var("TARGET").unwrap());
|
rsconf::set_env_value("BUILD_TARGET_TRIPLE", &env_var("TARGET").unwrap());
|
||||||
rsconf::set_env_value("BUILD_HOST_TRIPLE", &env::var("HOST").unwrap());
|
rsconf::set_env_value("BUILD_HOST_TRIPLE", &env_var("HOST").unwrap());
|
||||||
rsconf::set_env_value("BUILD_PROFILE", &env::var("PROFILE").unwrap());
|
rsconf::set_env_value("BUILD_PROFILE", &env_var("PROFILE").unwrap());
|
||||||
|
|
||||||
let version = &get_version(&env::current_dir().unwrap());
|
let version = &get_version(&env::current_dir().unwrap());
|
||||||
// Per https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script,
|
// Per https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script,
|
||||||
@@ -50,9 +50,6 @@ fn main() {
|
|||||||
#[cfg(feature = "gettext-extract")]
|
#[cfg(feature = "gettext-extract")]
|
||||||
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_FILE");
|
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_FILE");
|
||||||
|
|
||||||
rsconf::rebuild_if_path_changed("src/libc.c");
|
|
||||||
cc::Build::new().file("src/libc.c").compile("flibc.a");
|
|
||||||
|
|
||||||
let build = cc::Build::new();
|
let build = cc::Build::new();
|
||||||
let mut target = Target::new_from(build).unwrap();
|
let mut target = Target::new_from(build).unwrap();
|
||||||
// Keep verbose mode on until we've ironed out rust build script stuff
|
// Keep verbose mode on until we've ironed out rust build script stuff
|
||||||
@@ -117,7 +114,7 @@ fn detect_apple(_: &Target) -> bool {
|
|||||||
|
|
||||||
fn detect_cygwin(_: &Target) -> bool {
|
fn detect_cygwin(_: &Target) -> bool {
|
||||||
// Cygwin target is usually cross-compiled.
|
// Cygwin target is usually cross-compiled.
|
||||||
std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "cygwin"
|
env_var("CARGO_CFG_TARGET_OS").unwrap() == "cygwin"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detect if we're being compiled for a BSD-derived OS, allowing targeting code conditionally with
|
/// Detect if we're being compiled for a BSD-derived OS, allowing targeting code conditionally with
|
||||||
@@ -129,7 +126,7 @@ fn detect_cygwin(_: &Target) -> bool {
|
|||||||
fn detect_bsd(_: &Target) -> bool {
|
fn detect_bsd(_: &Target) -> bool {
|
||||||
// Instead of using `uname`, we can inspect the TARGET env variable set by Cargo. This lets us
|
// Instead of using `uname`, we can inspect the TARGET env variable set by Cargo. This lets us
|
||||||
// support cross-compilation scenarios.
|
// support cross-compilation scenarios.
|
||||||
let mut target = std::env::var("TARGET").unwrap();
|
let mut target = env_var("TARGET").unwrap();
|
||||||
if !target.chars().all(|c| c.is_ascii_lowercase()) {
|
if !target.chars().all(|c| c.is_ascii_lowercase()) {
|
||||||
target = target.to_ascii_lowercase();
|
target = target.to_ascii_lowercase();
|
||||||
}
|
}
|
||||||
@@ -172,10 +169,7 @@ fn has_small_stack(_: &Target) -> bool {
|
|||||||
// Modern macOS versions default to an 8 MiB main stack but legacy OS X have a 0.5 MiB one.
|
// Modern macOS versions default to an 8 MiB main stack but legacy OS X have a 0.5 MiB one.
|
||||||
let stack_size = unsafe { pthread_get_stacksize_np(pthread_self()) };
|
let stack_size = unsafe { pthread_get_stacksize_np(pthread_self()) };
|
||||||
const TWO_MIB: usize = 2 * 1024 * 1024 - 1;
|
const TWO_MIB: usize = 2 * 1024 * 1024 - 1;
|
||||||
match stack_size {
|
stack_size <= TWO_MIB
|
||||||
0..=TWO_MIB => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,51 +177,57 @@ fn setup_paths() {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use unix_path::{Path, PathBuf};
|
use unix_path::{Path, PathBuf};
|
||||||
|
|
||||||
fn get_path(name: &str, default: &str, onvar: &Path) -> PathBuf {
|
fn overridable_path(env_var_name: &str, f: impl FnOnce(Option<String>) -> PathBuf) -> PathBuf {
|
||||||
let mut var = PathBuf::from(env::var(name).unwrap_or(default.to_string()));
|
rsconf::rebuild_if_env_changed(env_var_name);
|
||||||
if var.is_relative() {
|
let path = f(env_var(env_var_name));
|
||||||
var = onvar.join(var);
|
rsconf::set_env_value(env_var_name, path.to_str().unwrap());
|
||||||
}
|
path
|
||||||
var
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix = PathBuf::from(env::var("PREFIX").unwrap_or("/usr/local".to_string()));
|
fn join_if_relative(parent_if_relative: &Path, path: String) -> PathBuf {
|
||||||
rsconf::rebuild_if_env_changed("PREFIX");
|
let path = PathBuf::from(path);
|
||||||
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
|
if path.is_relative() {
|
||||||
|
parent_if_relative.join(path)
|
||||||
let datadir = get_path("DATADIR", "share/", &prefix);
|
|
||||||
|
|
||||||
let sysconfdir = get_path(
|
|
||||||
"SYSCONFDIR",
|
|
||||||
// Embedded builds use "/etc," not "./share/etc".
|
|
||||||
if cfg!(feature = "embed-data") {
|
|
||||||
"/etc/"
|
|
||||||
} else {
|
} else {
|
||||||
"etc/"
|
path
|
||||||
},
|
}
|
||||||
&datadir,
|
}
|
||||||
);
|
|
||||||
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
|
let prefix = overridable_path("PREFIX", |env_prefix| {
|
||||||
rsconf::rebuild_if_env_changed("SYSCONFDIR");
|
PathBuf::from(env_prefix.unwrap_or("/usr/local".to_string()))
|
||||||
|
});
|
||||||
|
|
||||||
|
let datadir = join_if_relative(&prefix, env_var("DATADIR").unwrap_or("share/".to_string()));
|
||||||
|
rsconf::rebuild_if_env_changed("DATADIR");
|
||||||
|
#[cfg(not(feature = "embed-data"))]
|
||||||
|
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
|
||||||
|
|
||||||
|
overridable_path("SYSCONFDIR", |env_sysconfdir| {
|
||||||
|
join_if_relative(
|
||||||
|
&datadir,
|
||||||
|
env_sysconfdir.unwrap_or(
|
||||||
|
// Embedded builds use "/etc," not "./share/etc".
|
||||||
|
if cfg!(feature = "embed-data") {
|
||||||
|
"/etc/"
|
||||||
|
} else {
|
||||||
|
"etc/"
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
#[cfg(not(feature = "embed-data"))]
|
#[cfg(not(feature = "embed-data"))]
|
||||||
{
|
{
|
||||||
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
|
overridable_path("BINDIR", |env_bindir| {
|
||||||
rsconf::rebuild_if_env_changed("DATADIR");
|
join_if_relative(&prefix, env_bindir.unwrap_or("bin/".to_string()))
|
||||||
|
});
|
||||||
let bindir = get_path("BINDIR", "bin/", &prefix);
|
overridable_path("LOCALEDIR", |env_localedir| {
|
||||||
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
|
join_if_relative(&datadir, env_localedir.unwrap_or("locale/".to_string()))
|
||||||
rsconf::rebuild_if_env_changed("BINDIR");
|
});
|
||||||
|
overridable_path("DOCDIR", |env_docdir| {
|
||||||
let localedir = get_path("LOCALEDIR", "locale/", &datadir);
|
join_if_relative(&datadir, env_docdir.unwrap_or("doc/fish".to_string()))
|
||||||
let localedir = localedir.to_str().unwrap();
|
});
|
||||||
assert!(!localedir.is_empty(), "empty LOCALEDIR is not supported");
|
|
||||||
rsconf::set_env_value("LOCALEDIR", localedir);
|
|
||||||
rsconf::rebuild_if_env_changed("LOCALEDIR");
|
|
||||||
|
|
||||||
let docdir = get_path("DOCDIR", "doc/fish", &datadir);
|
|
||||||
rsconf::set_env_value("DOCDIR", docdir.to_str().unwrap());
|
|
||||||
rsconf::rebuild_if_env_changed("DOCDIR");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ fn get_version(src_dir: &Path) -> String {
|
|||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
if let Ok(var) = std::env::var("FISH_BUILD_VERSION") {
|
if let Some(var) = env_var("FISH_BUILD_VERSION") {
|
||||||
return var;
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,8 +43,16 @@ fi
|
|||||||
# Currently, all builds are debug builds.
|
# Currently, all builds are debug builds.
|
||||||
build_dir="$target_dir/debug"
|
build_dir="$target_dir/debug"
|
||||||
|
|
||||||
|
if [ -n "$FISH_TEST_MAX_CONCURRENCY" ]; then
|
||||||
|
export RUST_TEST_THREADS="$FISH_TEST_MAX_CONCURRENCY"
|
||||||
|
export CARGO_BUILD_JOBS="$FISH_TEST_MAX_CONCURRENCY"
|
||||||
|
fi
|
||||||
|
|
||||||
template_file=$(mktemp)
|
template_file=$(mktemp)
|
||||||
FISH_GETTEXT_EXTRACTION_FILE=$template_file cargo build --workspace --all-targets --features=gettext-extract
|
(
|
||||||
|
export FISH_GETTEXT_EXTRACTION_FILE="$template_file"
|
||||||
|
cargo build --workspace --all-targets --features=gettext-extract
|
||||||
|
)
|
||||||
if $lint; then
|
if $lint; then
|
||||||
PATH="$build_dir:$PATH" "$workspace_root/build_tools/style.fish" --all --check
|
PATH="$build_dir:$PATH" "$workspace_root/build_tools/style.fish" --all --check
|
||||||
for features in "" --no-default-features; do
|
for features in "" --no-default-features; do
|
||||||
|
|||||||
@@ -27,13 +27,21 @@ begin
|
|||||||
else
|
else
|
||||||
set rust_extraction_file (mktemp)
|
set rust_extraction_file (mktemp)
|
||||||
# We need to build to ensure that the proc macro for extracting strings runs.
|
# We need to build to ensure that the proc macro for extracting strings runs.
|
||||||
FISH_GETTEXT_EXTRACTION_FILE=$rust_extraction_file cargo check --features=gettext-extract
|
FISH_GETTEXT_EXTRACTION_FILE=$rust_extraction_file cargo check --no-default-features --features=gettext-extract
|
||||||
or exit 1
|
or exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
echo '# fish-section-tier1-from-rust'
|
function mark_section
|
||||||
|
set -l section_name $argv[1]
|
||||||
|
echo 'msgid "fish-section-'$section_name'"'
|
||||||
|
echo 'msgstr ""'
|
||||||
|
echo ''
|
||||||
|
end
|
||||||
|
|
||||||
|
mark_section tier1-from-rust
|
||||||
|
|
||||||
# Get rid of duplicates and sort.
|
# Get rid of duplicates and sort.
|
||||||
msguniq --no-wrap --strict --sort-output $rust_extraction_file
|
msguniq --no-wrap --sort-output $rust_extraction_file
|
||||||
or exit 1
|
or exit 1
|
||||||
|
|
||||||
if not set -l --query _flag_use_existing_template
|
if not set -l --query _flag_use_existing_template
|
||||||
@@ -77,13 +85,13 @@ begin
|
|||||||
# This regex handles explicit requests to translate a message. These are more important to translate
|
# This regex handles explicit requests to translate a message. These are more important to translate
|
||||||
# than messages which should be implicitly translated.
|
# than messages which should be implicitly translated.
|
||||||
set -l explicit_regex '.*\( *_ (([\'"]).+?(?<!\\\\)\\2) *\).*'
|
set -l explicit_regex '.*\( *_ (([\'"]).+?(?<!\\\\)\\2) *\).*'
|
||||||
echo "# fish-section-$tier-from-script-explicitly-added"
|
mark_section "$tier-from-script-explicitly-added"
|
||||||
extract_fish_script_messages_impl $explicit_regex $argv
|
extract_fish_script_messages_impl $explicit_regex $argv
|
||||||
|
|
||||||
# This regex handles descriptions for `complete` and `function` statements. These messages are not
|
# This regex handles descriptions for `complete` and `function` statements. These messages are not
|
||||||
# particularly important to translate. Hence the "implicit" label.
|
# particularly important to translate. Hence the "implicit" label.
|
||||||
set -l implicit_regex '^(?:\s|and |or )*(?:complete|function).*? (?:-d|--description) (([\'"]).+?(?<!\\\\)\\2).*'
|
set -l implicit_regex '^(?:\s|and |or )*(?:complete|function).*? (?:-d|--description) (([\'"]).+?(?<!\\\\)\\2).*'
|
||||||
echo "# fish-section-$tier-from-script-implicitly-added"
|
mark_section "$tier-from-script-implicitly-added"
|
||||||
extract_fish_script_messages_impl $implicit_regex $argv
|
extract_fish_script_messages_impl $implicit_regex $argv
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ milestone_number=$(
|
|||||||
gh_api_repo milestones?state=open |
|
gh_api_repo milestones?state=open |
|
||||||
jq '.[] | select(.title == "fish '"$version"'") | .number'
|
jq '.[] | select(.title == "fish '"$version"'") | .number'
|
||||||
)
|
)
|
||||||
gh_api_repo --method PATCH milestones/$milestone_number \
|
gh_api_repo milestones/$milestone_number --method PATCH \
|
||||||
--raw-field state=closed
|
--raw-field state=closed
|
||||||
|
|
||||||
next_patch_version=$(
|
next_patch_version=$(
|
||||||
@@ -244,7 +244,7 @@ next_patch_version=$(
|
|||||||
'
|
'
|
||||||
)
|
)
|
||||||
if [ -n "$next_patch_version" ]; then
|
if [ -n "$next_patch_version" ]; then
|
||||||
gh_api_repo --method POST milestones \
|
gh_api_repo milestones --method POST \
|
||||||
--raw-field title="fish $next_patch_version"
|
--raw-field title="fish $next_patch_version"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ if test $all = yes
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
set fish_files $workspace_root/{benchmarks,build_tools,etc,share}/**.fish
|
set fish_files $workspace_root/{benchmarks,build_tools,etc,share}/**.fish
|
||||||
set python_files {doc_src,share,tests}/**.py
|
set python_files $workspace_root
|
||||||
else
|
else
|
||||||
# Format the files specified as arguments.
|
# Format the files specified as arguments.
|
||||||
set -l files $argv
|
set -l files $argv
|
||||||
@@ -76,19 +76,19 @@ if set -q fish_files[1]
|
|||||||
end
|
end
|
||||||
|
|
||||||
if set -q python_files[1]
|
if set -q python_files[1]
|
||||||
if not type -q black
|
if not type -q ruff
|
||||||
echo
|
echo
|
||||||
echo $yellow'Please install `black` to style python'$normal
|
echo $yellow'Please install `ruff` to style python'$normal
|
||||||
exit 127
|
exit 127
|
||||||
end
|
end
|
||||||
echo === Running "$green"black"$normal"
|
echo === Running "$green"ruff format"$normal"
|
||||||
if set -l -q _flag_check
|
if set -l -q _flag_check
|
||||||
if not black --check $python_files
|
if not ruff format --check $python_files
|
||||||
echo $red"Python files are not formatted correctly."$normal
|
echo $red"Python files are not formatted correctly."$normal
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
black $python_files
|
ruff format $python_files
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ end
|
|||||||
echo === Running "$green"rustfmt"$normal"
|
echo === Running "$green"rustfmt"$normal"
|
||||||
if set -l -q _flag_check
|
if set -l -q _flag_check
|
||||||
if set -l -q _flag_all
|
if set -l -q _flag_all
|
||||||
if not cargo fmt --check
|
if not cargo fmt --all --check
|
||||||
echo $red"Rust files are not formatted correctly."$normal
|
echo $red"Rust files are not formatted correctly."$normal
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
@@ -115,7 +115,7 @@ if set -l -q _flag_check
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
if set -l -q _flag_all
|
if set -l -q _flag_all
|
||||||
cargo fmt
|
cargo fmt --all
|
||||||
else
|
else
|
||||||
if set -q rust_files[1]
|
if set -q rust_files[1]
|
||||||
rustfmt $rust_files
|
rustfmt $rust_files
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
set -gx LC_ALL C.UTF-8
|
set -gx LC_ALL C.UTF-8
|
||||||
|
|
||||||
set -l build_tools (status dirname)
|
set -l build_tools (status dirname)
|
||||||
set -g tmpdir
|
|
||||||
set -l po_dir $build_tools/../po
|
set -l po_dir $build_tools/../po
|
||||||
|
|
||||||
set -l extract
|
set -l extract
|
||||||
@@ -46,8 +45,8 @@ if test -z $argv[1]
|
|||||||
else
|
else
|
||||||
set -l po_dir_id (stat --format='%d:%i' -- $po_dir)
|
set -l po_dir_id (stat --format='%d:%i' -- $po_dir)
|
||||||
for arg in $argv
|
for arg in $argv
|
||||||
set -l arg_dir_id (stat --format='%d:%i' -- (dirname $arg))
|
set -l arg_dir_id (stat --format='%d:%i' -- (dirname $arg) 2>/dev/null)
|
||||||
if test $po_dir_id != $arg_dir_id
|
if test $po_dir_id != "$arg_dir_id"
|
||||||
echo "Argument $arg is not a file in the directory $(realpath $po_dir)."
|
echo "Argument $arg is not a file in the directory $(realpath $po_dir)."
|
||||||
echo "Non-option arguments must specify paths to files in this directory."
|
echo "Non-option arguments must specify paths to files in this directory."
|
||||||
echo ""
|
echo ""
|
||||||
@@ -99,44 +98,34 @@ if set -l --query _flag_dry_run
|
|||||||
cp -r $po_dir/* $tmpdir
|
cp -r $po_dir/* $tmpdir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# This is used to identify lines which should be set here via $header_lines.
|
||||||
|
# Make sure that this prefix does not appear elsewhere in the file and only contains characters
|
||||||
|
# without special meaning in a sed pattern.
|
||||||
|
set -g header_prefix "# fish-note-sections: "
|
||||||
|
|
||||||
|
function print_header
|
||||||
|
set -l header_lines \
|
||||||
|
"Translations are divided into sections, each starting with a fish-section-* pseudo-message." \
|
||||||
|
"The first few sections are more important." \
|
||||||
|
"Ignore the tier3 sections unless you have a lot of time."
|
||||||
|
for line in $header_lines
|
||||||
|
printf '%s%s\n' $header_prefix $line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function merge_po_files --argument-names template_file po_file
|
function merge_po_files --argument-names template_file po_file
|
||||||
msgmerge --no-wrap --update --no-fuzzy-matching --backup=none --quiet \
|
msgmerge --no-wrap --update --no-fuzzy-matching --backup=none --quiet \
|
||||||
$po_file $template_file
|
$po_file $template_file
|
||||||
or cleanup_exit
|
or cleanup_exit
|
||||||
set -l new_po_file (mktemp) # TODO Remove on failure.
|
set -l new_po_file (mktemp) # TODO Remove on failure.
|
||||||
|
# Remove obsolete messages instead of keeping them as #~ entries.
|
||||||
and msgattrib --no-wrap --no-obsolete -o $new_po_file $po_file
|
and msgattrib --no-wrap --no-obsolete -o $new_po_file $po_file
|
||||||
or cleanup_exit
|
or cleanup_exit
|
||||||
|
|
||||||
begin
|
begin
|
||||||
echo "# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment."
|
print_header
|
||||||
echo "# fish-note-sections: The first few sections are more important."
|
# Paste PO file without old header lines.
|
||||||
echo "# fish-note-sections: Ignore the tier3 sections unless you have a lot of time."
|
sed '/^'$header_prefix'/d' $new_po_file
|
||||||
sed -i '
|
|
||||||
/^# fish-note-sections:/d;
|
|
||||||
/^# fish-section-/d;
|
|
||||||
' $new_po_file
|
|
||||||
|
|
||||||
set -l next_line 1
|
|
||||||
set -l section
|
|
||||||
awk <$template_file '
|
|
||||||
/^# fish-section-\S*$/ {
|
|
||||||
section = $0
|
|
||||||
}
|
|
||||||
section != "" && /^msgid ".+"$/ {
|
|
||||||
print section
|
|
||||||
print $0
|
|
||||||
section = ""
|
|
||||||
}
|
|
||||||
' |
|
|
||||||
while read -l section
|
|
||||||
read -l msgid_line
|
|
||||||
set -l line_number (grep -m1 -Fxn $msgid_line $new_po_file | string split :)[1]
|
|
||||||
sed -n "$next_line,$(math $line_number - 1)"p $new_po_file
|
|
||||||
echo $section
|
|
||||||
set next_line $line_number
|
|
||||||
# set section
|
|
||||||
end
|
|
||||||
sed -n "$next_line,\$"p $new_po_file
|
|
||||||
end >$po_file
|
end >$po_file
|
||||||
rm $new_po_file
|
rm $new_po_file
|
||||||
end
|
end
|
||||||
@@ -149,7 +138,10 @@ for po_file in $po_files
|
|||||||
if test -e $po_file
|
if test -e $po_file
|
||||||
merge_po_files $template_file $po_file
|
merge_po_files $template_file $po_file
|
||||||
else
|
else
|
||||||
cp $template_file $po_file
|
begin
|
||||||
|
print_header
|
||||||
|
cat $template_file
|
||||||
|
end >$po_file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,21 @@
|
|||||||
use std::{borrow::Cow, env, path::Path};
|
use std::{borrow::Cow, env, os::unix::ffi::OsStrExt, path::Path};
|
||||||
|
|
||||||
|
pub fn env_var(name: &str) -> Option<String> {
|
||||||
|
let err = match env::var(name) {
|
||||||
|
Ok(p) => return Some(p),
|
||||||
|
Err(err) => err,
|
||||||
|
};
|
||||||
|
use env::VarError::*;
|
||||||
|
match err {
|
||||||
|
NotPresent => None,
|
||||||
|
NotUnicode(os_string) => {
|
||||||
|
panic!(
|
||||||
|
"Environment variable {name} is not valid Unicode: {:?}",
|
||||||
|
os_string.as_bytes()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn workspace_root() -> &'static Path {
|
pub fn workspace_root() -> &'static Path {
|
||||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
|||||||
@@ -14,12 +14,9 @@ fn main() {
|
|||||||
|
|
||||||
#[cfg(not(clippy))]
|
#[cfg(not(clippy))]
|
||||||
fn build_man(man_dir: &Path) {
|
fn build_man(man_dir: &Path) {
|
||||||
use std::{
|
use std::process::{Command, Stdio};
|
||||||
env,
|
|
||||||
process::{Command, Stdio},
|
|
||||||
};
|
|
||||||
|
|
||||||
use fish_build_helper::workspace_root;
|
use fish_build_helper::{env_var, workspace_root};
|
||||||
|
|
||||||
let workspace_root = workspace_root();
|
let workspace_root = workspace_root();
|
||||||
|
|
||||||
@@ -47,7 +44,7 @@ fn build_man(man_dir: &Path) {
|
|||||||
let _ = std::fs::create_dir_all(sec1_str);
|
let _ = std::fs::create_dir_all(sec1_str);
|
||||||
|
|
||||||
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
|
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
|
||||||
if env::var("FISH_BUILD_DOCS") == Ok("0".to_string()) {
|
if env_var("FISH_BUILD_DOCS") == Some("0".to_string()) {
|
||||||
rsconf::warn!("Skipping man pages because $FISH_BUILD_DOCS is set to 0");
|
rsconf::warn!("Skipping man pages because $FISH_BUILD_DOCS is set to 0");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,7 +61,7 @@ fn build_man(man_dir: &Path) {
|
|||||||
.spawn()
|
.spawn()
|
||||||
{
|
{
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
if env::var("FISH_BUILD_DOCS") == Ok("1".to_string()) {
|
if env_var("FISH_BUILD_DOCS") == Some("1".to_string()) {
|
||||||
panic!("Could not find sphinx-build to build man pages.\nInstall sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0.");
|
panic!("Could not find sphinx-build to build man pages.\nInstall sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0.");
|
||||||
}
|
}
|
||||||
rsconf::warn!("Cannot find sphinx-build to build man pages.");
|
rsconf::warn!("Cannot find sphinx-build to build man pages.");
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use std::{
|
use std::{
|
||||||
env,
|
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::Command,
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use fish_build_helper::env_var;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cache_dir =
|
let cache_dir =
|
||||||
PathBuf::from(fish_build_helper::fish_build_dir()).join("fish-localization-map-cache");
|
PathBuf::from(fish_build_helper::fish_build_dir()).join("fish-localization-map-cache");
|
||||||
@@ -27,14 +28,18 @@ fn embed_localizations(cache_dir: &Path) {
|
|||||||
std::fs::create_dir_all(cache_dir).unwrap();
|
std::fs::create_dir_all(cache_dir).unwrap();
|
||||||
|
|
||||||
let localization_map_path =
|
let localization_map_path =
|
||||||
Path::new(&env::var("OUT_DIR").unwrap()).join("localization_maps.rs");
|
Path::new(&env_var("OUT_DIR").unwrap()).join("localization_maps.rs");
|
||||||
let mut localization_map_file = BufWriter::new(File::create(&localization_map_path).unwrap());
|
let mut localization_map_file = BufWriter::new(File::create(&localization_map_path).unwrap());
|
||||||
|
|
||||||
// This will become a map which maps from language identifiers to maps containing localizations
|
// This will become a map which maps from language identifiers to maps containing localizations
|
||||||
// for the respective language.
|
// for the respective language.
|
||||||
let mut catalogs = phf_codegen::Map::new();
|
let mut catalogs = phf_codegen::Map::new();
|
||||||
|
|
||||||
match Command::new("msgfmt").arg("-h").status() {
|
match Command::new("msgfmt")
|
||||||
|
.arg("-h")
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.status()
|
||||||
|
{
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
rsconf::warn!(
|
rsconf::warn!(
|
||||||
"Cannot find msgfmt to build gettext message catalogs. Localization will not work."
|
"Cannot find msgfmt to build gettext message catalogs. Localization will not work."
|
||||||
@@ -97,6 +102,12 @@ fn embed_localizations(cache_dir: &Path) {
|
|||||||
.arg(&po_file_path)
|
.arg(&po_file_path)
|
||||||
.output()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
if !output.status.success() {
|
||||||
|
panic!(
|
||||||
|
"msgfmt failed:\n{}",
|
||||||
|
String::from_utf8(output.stderr).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
let mo_data = output.stdout;
|
let mo_data = output.stdout;
|
||||||
|
|
||||||
// Extract map from MO data.
|
// Extract map from MO data.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub enum Arg<'a> {
|
|||||||
#[cfg(feature = "widestring")]
|
#[cfg(feature = "widestring")]
|
||||||
WString(WString),
|
WString(WString),
|
||||||
UInt(u64),
|
UInt(u64),
|
||||||
SInt(i64, u8), // signed integers track their width as the number of bits
|
SInt(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
USizeRef(&'a mut usize), // for use with %n
|
USizeRef(&'a mut usize), // for use with %n
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ pub fn as_str<'s>(&'s self, storage: &'s mut String) -> Result<&'s str, Error>
|
|||||||
pub fn as_uint(&self) -> Result<u64, Error> {
|
pub fn as_uint(&self) -> Result<u64, Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Arg::UInt(u) => Ok(u),
|
Arg::UInt(u) => Ok(u),
|
||||||
Arg::SInt(i, _w) => i.try_into().map_err(|_| Error::Overflow),
|
Arg::SInt(i) => i.try_into().map_err(|_| Error::Overflow),
|
||||||
_ => Err(Error::BadArgType),
|
_ => Err(Error::BadArgType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,25 +68,18 @@ pub fn as_uint(&self) -> Result<u64, Error> {
|
|||||||
pub fn as_sint(&self) -> Result<i64, Error> {
|
pub fn as_sint(&self) -> Result<i64, Error> {
|
||||||
match *self {
|
match *self {
|
||||||
Arg::UInt(u) => u.try_into().map_err(|_| Error::Overflow),
|
Arg::UInt(u) => u.try_into().map_err(|_| Error::Overflow),
|
||||||
Arg::SInt(i, _w) => Ok(i),
|
Arg::SInt(i) => Ok(i),
|
||||||
_ => Err(Error::BadArgType),
|
_ => Err(Error::BadArgType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a signed value, then return the sign (true if negative) and the magnitude,
|
/// Unwraps [`Arg::UInt`] to [`u64`].
|
||||||
// masked to the value's width. This allows for e.g. -1 to be returned as 0xFF, 0xFFFF, etc.
|
/// Unwraps [`Arg::SInt`] and casts the [`i64`] to [`u64`].
|
||||||
// depending on the original width.
|
/// Calling this on other variants of `[Arg]` is an error.
|
||||||
// If this is an unsigned value, simply return (false, u64).
|
pub fn as_wrapping_sint(&self) -> Result<u64, Error> {
|
||||||
pub fn as_wrapping_sint(&self) -> Result<(bool, u64), Error> {
|
|
||||||
match *self {
|
match *self {
|
||||||
Arg::UInt(u) => Ok((false, u)),
|
Arg::UInt(u) => Ok(u),
|
||||||
Arg::SInt(i, w) => {
|
Arg::SInt(i) => Ok(i as u64),
|
||||||
// Need to shift twice in case w is 64.
|
|
||||||
debug_assert!(w > 0);
|
|
||||||
let mask = ((1u64 << (w - 1)) << 1).wrapping_sub(1);
|
|
||||||
let ui = (i as u64) & mask;
|
|
||||||
Ok((i < 0, ui))
|
|
||||||
}
|
|
||||||
_ => Err(Error::BadArgType),
|
_ => Err(Error::BadArgType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +90,7 @@ pub fn as_float(&self) -> Result<f64, Error> {
|
|||||||
match *self {
|
match *self {
|
||||||
Arg::Float(f) => Ok(f),
|
Arg::Float(f) => Ok(f),
|
||||||
Arg::UInt(u) => Ok(u as f64),
|
Arg::UInt(u) => Ok(u as f64),
|
||||||
Arg::SInt(i, _w) => Ok(i as f64),
|
Arg::SInt(i) => Ok(i as f64),
|
||||||
_ => Err(Error::BadArgType),
|
_ => Err(Error::BadArgType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,7 +174,7 @@ macro_rules! impl_to_arg {
|
|||||||
$(
|
$(
|
||||||
impl<'a> ToArg<'a> for $t {
|
impl<'a> ToArg<'a> for $t {
|
||||||
fn to_arg(self) -> Arg<'a> {
|
fn to_arg(self) -> Arg<'a> {
|
||||||
Arg::SInt(self as i64, <$t>::BITS as u8)
|
Arg::SInt(self as i64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
@@ -211,8 +204,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_arg() {
|
fn test_to_arg() {
|
||||||
const SIZE_WIDTH: u8 = isize::BITS as u8;
|
|
||||||
|
|
||||||
assert!(matches!("test".to_arg(), Arg::Str("test")));
|
assert!(matches!("test".to_arg(), Arg::Str("test")));
|
||||||
assert!(matches!(String::from("test").to_arg(), Arg::Str(_)));
|
assert!(matches!(String::from("test").to_arg(), Arg::Str(_)));
|
||||||
#[cfg(feature = "widestring")]
|
#[cfg(feature = "widestring")]
|
||||||
@@ -224,17 +215,17 @@ fn test_to_arg() {
|
|||||||
assert!(matches!('x'.to_arg(), Arg::UInt(120)));
|
assert!(matches!('x'.to_arg(), Arg::UInt(120)));
|
||||||
let mut usize_val: usize = 0;
|
let mut usize_val: usize = 0;
|
||||||
assert!(matches!((&mut usize_val).to_arg(), Arg::USizeRef(_)));
|
assert!(matches!((&mut usize_val).to_arg(), Arg::USizeRef(_)));
|
||||||
assert!(matches!(42i8.to_arg(), Arg::SInt(42, 8)));
|
assert!(matches!(42i8.to_arg(), Arg::SInt(42)));
|
||||||
assert!(matches!(42i16.to_arg(), Arg::SInt(42, 16)));
|
assert!(matches!(42i16.to_arg(), Arg::SInt(42)));
|
||||||
assert!(matches!(42i32.to_arg(), Arg::SInt(42, 32)));
|
assert!(matches!(42i32.to_arg(), Arg::SInt(42)));
|
||||||
assert!(matches!(42i64.to_arg(), Arg::SInt(42, 64)));
|
assert!(matches!(42i64.to_arg(), Arg::SInt(42)));
|
||||||
assert!(matches!(42isize.to_arg(), Arg::SInt(42, SIZE_WIDTH)));
|
assert!(matches!(42isize.to_arg(), Arg::SInt(42)));
|
||||||
|
|
||||||
assert_eq!((-42i8).to_arg(), Arg::SInt(-42, 8));
|
assert_eq!((-42i8).to_arg(), Arg::SInt(-42));
|
||||||
assert_eq!((-42i16).to_arg(), Arg::SInt(-42, 16));
|
assert_eq!((-42i16).to_arg(), Arg::SInt(-42));
|
||||||
assert_eq!((-42i32).to_arg(), Arg::SInt(-42, 32));
|
assert_eq!((-42i32).to_arg(), Arg::SInt(-42));
|
||||||
assert_eq!((-42i64).to_arg(), Arg::SInt(-42, 64));
|
assert_eq!((-42i64).to_arg(), Arg::SInt(-42));
|
||||||
assert_eq!((-42isize).to_arg(), Arg::SInt(-42, SIZE_WIDTH));
|
assert_eq!((-42isize).to_arg(), Arg::SInt(-42));
|
||||||
|
|
||||||
assert!(matches!(42u8.to_arg(), Arg::UInt(42)));
|
assert!(matches!(42u8.to_arg(), Arg::UInt(42)));
|
||||||
assert!(matches!(42u16.to_arg(), Arg::UInt(42)));
|
assert!(matches!(42u16.to_arg(), Arg::UInt(42)));
|
||||||
|
|||||||
@@ -472,7 +472,7 @@ pub fn sprintf_locale(
|
|||||||
// If someone passes us a negative value, format it with the width
|
// If someone passes us a negative value, format it with the width
|
||||||
// we were given.
|
// we were given.
|
||||||
let lower = conv_spec.is_lower();
|
let lower = conv_spec.is_lower();
|
||||||
let (_, uint) = arg.as_wrapping_sint()?;
|
let uint = arg.as_wrapping_sint()?;
|
||||||
if uint != 0 {
|
if uint != 0 {
|
||||||
if flags.alt_form {
|
if flags.alt_form {
|
||||||
prefix = if lower { "0x" } else { "0X" };
|
prefix = if lower { "0x" } else { "0X" };
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ fn write_str(&mut self, _s: &str) -> fmt::Result {
|
|||||||
#[test]
|
#[test]
|
||||||
fn smoke() {
|
fn smoke() {
|
||||||
assert_fmt!("Hello, %s!", "world" => "Hello, world!");
|
assert_fmt!("Hello, %s!", "world" => "Hello, world!");
|
||||||
assert_fmt!("Hello, %ls!", "world" => "Hello, world!");
|
assert_fmt!("Hello, %ls!", "world" => "Hello, world!"); // length modifier
|
||||||
assert_fmt!("Hello, world! %d %%%%", 3 => "Hello, world! 3 %%");
|
assert_fmt!("Hello, world! %d %%%%", 3 => "Hello, world! 3 %%");
|
||||||
assert_fmt!("" => "");
|
assert_fmt!("" => "");
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ fn test_int() {
|
|||||||
assert_fmt!("%d", -123 => "-123");
|
assert_fmt!("%d", -123 => "-123");
|
||||||
assert_fmt!("~%d~", 148 => "~148~");
|
assert_fmt!("~%d~", 148 => "~148~");
|
||||||
assert_fmt!("00%dxx", -91232 => "00-91232xx");
|
assert_fmt!("00%dxx", -91232 => "00-91232xx");
|
||||||
assert_fmt!("%x", -9232 => "ffffdbf0");
|
assert_fmt!("%x", -9232 => "ffffffffffffdbf0");
|
||||||
assert_fmt!("%X", 432 => "1B0");
|
assert_fmt!("%X", 432 => "1B0");
|
||||||
assert_fmt!("%09X", 432 => "0000001B0");
|
assert_fmt!("%09X", 432 => "0000001B0");
|
||||||
assert_fmt!("%9X", 432 => " 1B0");
|
assert_fmt!("%9X", 432 => " 1B0");
|
||||||
@@ -234,6 +234,7 @@ fn test_int() {
|
|||||||
assert_fmt!("%2o", 4 => " 4");
|
assert_fmt!("%2o", 4 => " 4");
|
||||||
assert_fmt!("% 12d", -4 => " -4");
|
assert_fmt!("% 12d", -4 => " -4");
|
||||||
assert_fmt!("% 12d", 48 => " 48");
|
assert_fmt!("% 12d", 48 => " 48");
|
||||||
|
// with length modifier
|
||||||
assert_fmt!("%ld", -4_i64 => "-4");
|
assert_fmt!("%ld", -4_i64 => "-4");
|
||||||
assert_fmt!("%lld", -4_i64 => "-4");
|
assert_fmt!("%lld", -4_i64 => "-4");
|
||||||
assert_fmt!("%lX", -4_i64 => "FFFFFFFFFFFFFFFC");
|
assert_fmt!("%lX", -4_i64 => "FFFFFFFFFFFFFFFC");
|
||||||
@@ -248,6 +249,7 @@ fn test_int() {
|
|||||||
assert_fmt!("%9X", 492 => " 1EC");
|
assert_fmt!("%9X", 492 => " 1EC");
|
||||||
assert_fmt!("% 12u", 4 => " 4");
|
assert_fmt!("% 12u", 4 => " 4");
|
||||||
assert_fmt!("% 12u", 48 => " 48");
|
assert_fmt!("% 12u", 48 => " 48");
|
||||||
|
// with length modifier
|
||||||
assert_fmt!("%lu", 4_u64 => "4");
|
assert_fmt!("%lu", 4_u64 => "4");
|
||||||
assert_fmt!("%llu", 4_u64 => "4");
|
assert_fmt!("%llu", 4_u64 => "4");
|
||||||
assert_fmt!("%lX", 4_u64 => "4");
|
assert_fmt!("%lX", 4_u64 => "4");
|
||||||
@@ -414,6 +416,7 @@ fn test_float() {
|
|||||||
assert_fmt1!("%f", 0.0, "0.000000");
|
assert_fmt1!("%f", 0.0, "0.000000");
|
||||||
assert_fmt1!("%g", 0.0, "0");
|
assert_fmt1!("%g", 0.0, "0");
|
||||||
assert_fmt1!("%#g", 0.0, "0.00000");
|
assert_fmt1!("%#g", 0.0, "0.00000");
|
||||||
|
// with length modifier
|
||||||
assert_fmt1!("%la", 0.0, "0x0p+0");
|
assert_fmt1!("%la", 0.0, "0x0p+0");
|
||||||
assert_fmt1!("%le", 0.0, "0.000000e+00");
|
assert_fmt1!("%le", 0.0, "0.000000e+00");
|
||||||
assert_fmt1!("%lf", 0.0, "0.000000");
|
assert_fmt1!("%lf", 0.0, "0.000000");
|
||||||
@@ -430,7 +433,7 @@ fn test_float() {
|
|||||||
assert_fmt1!("%.4f", 1.03125, "1.0312"); /* 0x1.08p0 */
|
assert_fmt1!("%.4f", 1.03125, "1.0312"); /* 0x1.08p0 */
|
||||||
assert_fmt1!("%.2f", 1.375, "1.38");
|
assert_fmt1!("%.2f", 1.375, "1.38");
|
||||||
assert_fmt1!("%.1f", 1.375, "1.4");
|
assert_fmt1!("%.1f", 1.375, "1.4");
|
||||||
assert_fmt1!("%.1lf", 1.375, "1.4");
|
assert_fmt1!("%.1lf", 1.375, "1.4"); // length modifier
|
||||||
assert_fmt1!("%.15f", 1.1, "1.100000000000000");
|
assert_fmt1!("%.15f", 1.1, "1.100000000000000");
|
||||||
assert_fmt1!("%.16f", 1.1, "1.1000000000000001");
|
assert_fmt1!("%.16f", 1.1, "1.1000000000000001");
|
||||||
assert_fmt1!("%.17f", 1.1, "1.10000000000000009");
|
assert_fmt1!("%.17f", 1.1, "1.10000000000000009");
|
||||||
@@ -755,8 +758,8 @@ fn test_errors() {
|
|||||||
sprintf_err!("%1", => BadFormatString);
|
sprintf_err!("%1", => BadFormatString);
|
||||||
sprintf_err!("%%%k", => BadFormatString);
|
sprintf_err!("%%%k", => BadFormatString);
|
||||||
sprintf_err!("%B", => BadFormatString);
|
sprintf_err!("%B", => BadFormatString);
|
||||||
sprintf_err!("%lC", 'q' => BadFormatString);
|
sprintf_err!("%lC", 'q' => BadFormatString); // length modifier
|
||||||
sprintf_err!("%lS", 'q' => BadFormatString);
|
sprintf_err!("%lS", 'q' => BadFormatString); // length modifier
|
||||||
sprintf_err!("%d", => MissingArg);
|
sprintf_err!("%d", => MissingArg);
|
||||||
sprintf_err!("%d %u", 1 => MissingArg);
|
sprintf_err!("%d %u", 1 => MissingArg);
|
||||||
sprintf_err!("%*d", 5 => MissingArg);
|
sprintf_err!("%*d", 5 => MissingArg);
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ The typical use is to run something, stop it with ctrl-z, and then continue it i
|
|||||||
|
|
||||||
If only 123 and 789 exist, it will still background them and print an error about 456.
|
If only 123 and 789 exist, it will still background them and print an error about 456.
|
||||||
|
|
||||||
``bg 123 banana`` or ``bg banana 123`` will complain that "banana" is not a valid job specifier.
|
``bg 123 banana`` or ``bg banana 123`` will complain that "banana" is not a valid process ID.
|
||||||
|
|
||||||
``bg %2`` will background job 2.
|
``bg %2`` will background job 2.
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ This refuses to store any immediate "vault", "mysql" or "ls" calls. Commands sta
|
|||||||
|
|
||||||
function fish_should_add_to_history
|
function fish_should_add_to_history
|
||||||
# I don't want `git pull`s in my history when I'm in a specific repository
|
# I don't want `git pull`s in my history when I'm in a specific repository
|
||||||
if string match -qr '^git pull'
|
if string match -qr '^git pull' -- "$argv"
|
||||||
and string match -qr "^/home/me/my-secret-project/" -- (pwd -P)
|
and string match -qr "^/home/me/my-secret-project/" -- (pwd -P)
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ A function is a list of commands that will be executed when the name of the func
|
|||||||
The following options are available:
|
The following options are available:
|
||||||
|
|
||||||
**-a** *NAMES* or **--argument-names** *NAMES*
|
**-a** *NAMES* or **--argument-names** *NAMES*
|
||||||
Has to be the last option. Assigns the value of successive command-line arguments to the names given in *NAMES* (separated by space). These are the same arguments given in :envvar:`argv`, and are still available there. See also :ref:`Argument Handling <variables-argv>`.
|
Assigns the value of successive command-line arguments to the names given in *NAMES* (separated by spaces). These are the same arguments given in :envvar:`argv`, and are still available there (unless ``--inherit-variable argv`` was used or one of the given *NAMES* is ``argv``). See also :ref:`Argument Handling <variables-argv>`.
|
||||||
|
|
||||||
**-d** *DESCRIPTION* or **--description** *DESCRIPTION*
|
**-d** *DESCRIPTION* or **--description** *DESCRIPTION*
|
||||||
A description of what the function does, suitable as a completion description.
|
A description of what the function does, suitable as a completion description.
|
||||||
@@ -40,7 +40,7 @@ The following options are available:
|
|||||||
Run this function when the variable *VARIABLE_NAME* changes value. Note that :program:`fish` makes no guarantees on any particular timing or even that the function will be run for every single ``set``. Rather it will be run when the variable has been set at least once, possibly skipping some values or being run when the variable has been set to the same value (except for universal variables set in other shells - only changes in the value will be picked up for those).
|
Run this function when the variable *VARIABLE_NAME* changes value. Note that :program:`fish` makes no guarantees on any particular timing or even that the function will be run for every single ``set``. Rather it will be run when the variable has been set at least once, possibly skipping some values or being run when the variable has been set to the same value (except for universal variables set in other shells - only changes in the value will be picked up for those).
|
||||||
|
|
||||||
**-j** *PID* or **--on-job-exit** *PID*
|
**-j** *PID* or **--on-job-exit** *PID*
|
||||||
Run this function when the job containing a child process with the given process identifier *PID* exits. Instead of a PID, the string 'caller' can be specified. This is only allowed when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution.
|
Run this function when the job containing a child process with the given process ID *PID* exits. Instead of a PID, the string 'caller' can be specified. This is only allowed when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution.
|
||||||
This will not trigger for :doc:`disowned <disown>` jobs.
|
This will not trigger for :doc:`disowned <disown>` jobs.
|
||||||
|
|
||||||
**-p** *PID* or **--on-process-exit** *PID*
|
**-p** *PID* or **--on-process-exit** *PID*
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ LABEL org.opencontainers.image.source=https://github.com/fish-shell/fish-shell
|
|||||||
|
|
||||||
ENV LANG=C.UTF-8
|
ENV LANG=C.UTF-8
|
||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV PIP_ROOT_USER_ACTION=ignore
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
|
cmake ninja \
|
||||||
bash \
|
bash \
|
||||||
cargo \
|
cargo \
|
||||||
g++ \
|
g++ \
|
||||||
@@ -14,11 +16,15 @@ RUN apk add --no-cache \
|
|||||||
musl-dev \
|
musl-dev \
|
||||||
pcre2-dev \
|
pcre2-dev \
|
||||||
py3-pexpect \
|
py3-pexpect \
|
||||||
|
py3-pip \
|
||||||
python3 \
|
python3 \
|
||||||
rust \
|
rust \
|
||||||
|
rustfmt \
|
||||||
sudo \
|
sudo \
|
||||||
tmux
|
tmux
|
||||||
|
|
||||||
|
RUN pip install --break-system-packages black
|
||||||
|
|
||||||
RUN addgroup -g 1000 fishuser
|
RUN addgroup -g 1000 fishuser
|
||||||
|
|
||||||
RUN adduser \
|
RUN adduser \
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ ENV LC_ALL=C.UTF-8
|
|||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
g++-multilib \
|
g++-multilib \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
|
|||||||
@@ -6,14 +6,17 @@ ENV LC_ALL=C.UTF-8
|
|||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
|
cmake ninja-build \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
cargo \
|
cargo \
|
||||||
clang \
|
clang \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
rustc \
|
rustc \
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ ENV LC_ALL=C.UTF-8
|
|||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
cargo \
|
cargo \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ ENV LC_ALL=C.UTF-8
|
|||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
|
cmake ninja-build \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
cargo \
|
cargo \
|
||||||
file \
|
file \
|
||||||
g++ \
|
g++ \
|
||||||
@@ -15,10 +17,11 @@ RUN apt-get update \
|
|||||||
git \
|
git \
|
||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
rust \
|
rustc \
|
||||||
sudo \
|
sudo \
|
||||||
tmux \
|
tmux \
|
||||||
&& locale-gen en_US.UTF-8 \
|
&& locale-gen en_US.UTF-8 \
|
||||||
|
|||||||
@@ -5,13 +5,16 @@ ENV LANG=C.UTF-8
|
|||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
clang \
|
clang \
|
||||||
|
curl \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
sudo \
|
sudo \
|
||||||
|
|||||||
@@ -5,13 +5,16 @@ ENV LANG=C.UTF-8
|
|||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
clang \
|
clang \
|
||||||
|
curl \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
sudo \
|
sudo \
|
||||||
|
|||||||
@@ -5,22 +5,28 @@ ENV LANG=C.UTF-8
|
|||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
|
cmake ninja-build \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
cargo \
|
cargo \
|
||||||
clang \
|
clang \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
rustc \
|
rustc \
|
||||||
sudo \
|
sudo \
|
||||||
tmux \
|
tmux \
|
||||||
|
python3-pip \
|
||||||
&& locale-gen en_US.UTF-8 \
|
&& locale-gen en_US.UTF-8 \
|
||||||
&& apt-get clean
|
&& apt-get clean
|
||||||
|
|
||||||
|
RUN pip install black
|
||||||
|
|
||||||
RUN groupadd -g 1000 fishuser \
|
RUN groupadd -g 1000 fishuser \
|
||||||
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
&& useradd -p $(openssl passwd -1 fish) -d /home/fishuser -m -u 1000 -g 1000 fishuser \
|
||||||
&& adduser fishuser sudo \
|
&& adduser fishuser sudo \
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ ENV LANG=C.UTF-8
|
|||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get -y install \
|
&& apt-get -y install --no-install-recommends \
|
||||||
|
adduser \
|
||||||
build-essential \
|
build-essential \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
libpcre2-dev \
|
libpcre2-dev \
|
||||||
locales \
|
locales \
|
||||||
|
openssl \
|
||||||
python3 \
|
python3 \
|
||||||
python3-pexpect \
|
python3-pexpect \
|
||||||
tmux \
|
tmux \
|
||||||
|
|||||||
1670
po/pt_BR.po
1670
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
1758
po/zh_CN.po
1758
po/zh_CN.po
File diff suppressed because it is too large
Load Diff
@@ -1,46 +1,16 @@
|
|||||||
function __fish_bind_test1
|
set -l bind_optspecs \
|
||||||
set -l args
|
a/all \
|
||||||
set -l use_keys no
|
e/erase \
|
||||||
for i in (commandline -pxc)
|
M/mode= \
|
||||||
switch $i
|
m/sets-mode= \
|
||||||
case -k --k --ke --key
|
preset \
|
||||||
set use_keys yes
|
s/silent \
|
||||||
|
user
|
||||||
case "-*"
|
|
||||||
|
|
||||||
case "*"
|
|
||||||
set -a args $i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
switch $use_keys
|
|
||||||
case yes
|
|
||||||
switch (count $args)
|
|
||||||
case 1
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function __fish_bind_test2
|
|
||||||
set -l args
|
|
||||||
for i in (commandline -pxc)
|
|
||||||
switch $i
|
|
||||||
case "-*"
|
|
||||||
|
|
||||||
case "*"
|
|
||||||
set -a args $i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
switch (count $args)
|
|
||||||
case 2
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
function __fish_bind_has_keys --inherit-variable bind_optspecs
|
||||||
|
argparse $bind_optspecs -- $argv 2>/dev/null
|
||||||
|
or return
|
||||||
|
test (count $argv) -ge 2
|
||||||
end
|
end
|
||||||
|
|
||||||
complete -c bind -f
|
complete -c bind -f
|
||||||
@@ -56,11 +26,10 @@ complete -c bind -s s -l silent -d 'Operate silently'
|
|||||||
complete -c bind -l preset -d 'Operate on preset bindings'
|
complete -c bind -l preset -d 'Operate on preset bindings'
|
||||||
complete -c bind -l user -d 'Operate on user bindings'
|
complete -c bind -l user -d 'Operate on user bindings'
|
||||||
|
|
||||||
complete -c bind -n __fish_bind_test2 -a '(bind --function-names)' -d 'Function name' -x
|
complete -c bind -n '__fish_bind_has_keys (commandline -pcx)' -a '(bind --function-names)' -d 'Function name' -x
|
||||||
|
|
||||||
function __fish_bind_complete
|
function __fish_bind_complete --inherit-variable bind_optspecs
|
||||||
argparse M/mode= m/sets-mode= preset user s/silent \
|
argparse $bind_optspecs -- (commandline -xpc)[2..] 2>/dev/null
|
||||||
a/all function-names list-modes e/erase -- (commandline -xpc)[2..] 2>/dev/null
|
|
||||||
or return 1
|
or return 1
|
||||||
set -l token (commandline -ct)
|
set -l token (commandline -ct)
|
||||||
if test (count $argv) = 0 && set -l prefix (string match -r -- '(.*,)?(ctrl-|alt-|shift-|super-)*' $token)
|
if test (count $argv) = 0 && set -l prefix (string match -r -- '(.*,)?(ctrl-|alt-|shift-|super-)*' $token)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ complete -c find -o empty -d "File is empty and is either a regular file or a di
|
|||||||
complete -c find -o executable -d "File is executable"
|
complete -c find -o executable -d "File is executable"
|
||||||
complete -c find -o false -d "Always false"
|
complete -c find -o false -d "Always false"
|
||||||
complete -c find -o fstype -d "File is on filesystem of specified type" -a "(__fish_print_filesystems)" -x
|
complete -c find -o fstype -d "File is on filesystem of specified type" -a "(__fish_print_filesystems)" -x
|
||||||
complete -c find -o gid -d "Numeric group id of file" -x -a "(__fish_complete_group_ids)"
|
complete -c find -o gid -d "Numeric group ID of file" -x -a "(__fish_complete_group_ids)"
|
||||||
complete -c find -o group -d "Group name of file" -x -a "(__fish_complete_groups)"
|
complete -c find -o group -d "Group name of file" -x -a "(__fish_complete_groups)"
|
||||||
|
|
||||||
complete -c find -o ilname -d "File is symlink matching specified case insensitive pattern" -x
|
complete -c find -o ilname -d "File is symlink matching specified case insensitive pattern" -x
|
||||||
|
|||||||
@@ -1468,7 +1468,7 @@ complete -x -c git -n '__fish_git_using_command daemon' -l user-path -d 'Allow ~
|
|||||||
complete -f -c git -n '__fish_git_using_command daemon' -l verbose -d 'Log all details'
|
complete -f -c git -n '__fish_git_using_command daemon' -l verbose -d 'Log all details'
|
||||||
complete -f -c git -n '__fish_git_using_command daemon' -l reuseaddr -d 'Reuse address when binding to listening server'
|
complete -f -c git -n '__fish_git_using_command daemon' -l reuseaddr -d 'Reuse address when binding to listening server'
|
||||||
complete -f -c git -n '__fish_git_using_command daemon' -l detach -d 'Detach from shell'
|
complete -f -c git -n '__fish_git_using_command daemon' -l detach -d 'Detach from shell'
|
||||||
complete -x -c git -n '__fish_git_using_command daemon' -l reuseaddr -d 'Save the process id in file'
|
complete -x -c git -n '__fish_git_using_command daemon' -l reuseaddr -d 'Save the process ID in file'
|
||||||
complete -x -c git -n '__fish_git_using_command daemon' -l user -d 'Change daemon\'s uid'
|
complete -x -c git -n '__fish_git_using_command daemon' -l user -d 'Change daemon\'s uid'
|
||||||
complete -x -c git -n '__fish_git_using_command daemon' -l group -d 'Change daemon\'s gid'
|
complete -x -c git -n '__fish_git_using_command daemon' -l group -d 'Change daemon\'s gid'
|
||||||
complete -x -c git -n '__fish_git_using_command daemon' -l enable -a 'upload-pack upload-archive receive-pack' -d 'Enable service'
|
complete -x -c git -n '__fish_git_using_command daemon' -l enable -a 'upload-pack upload-archive receive-pack' -d 'Enable service'
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
if test -d "$__fish_data_dir/man/man1/"
|
complete -c help -x -a '(__fish_print_commands)' -d 'Help for this command'
|
||||||
complete -c help -x -a '(__fish_print_commands)' -d 'Help for this command'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Help topics in index.html
|
# Help topics in index.html
|
||||||
# This was semi-automated with `grep 'class="anchor"' -A1 /usr/share/doc/fish/index.html
|
# This was semi-automated with `grep 'class="anchor"' -A1 /usr/share/doc/fish/index.html
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ if string match -eq 'GNU coreutils' (id --version 2>&1)
|
|||||||
complete id -s Z -l context -d "Print security context"
|
complete id -s Z -l context -d "Print security context"
|
||||||
complete id -s z -l zero -d "Delimit entries with NUL"
|
complete id -s z -l zero -d "Delimit entries with NUL"
|
||||||
complete id -s n -l name -d "Print name, not number"
|
complete id -s n -l name -d "Print name, not number"
|
||||||
complete id -s g -l group -d "Print effective group id"
|
complete id -s g -l group -d "Print effective group ID"
|
||||||
complete id -s G -l groups -d "Print all group ids"
|
complete id -s G -l groups -d "Print all group ids"
|
||||||
complete id -s r -l real -d "Print real ID, not effective"
|
complete id -s r -l real -d "Print real ID, not effective"
|
||||||
complete id -s u -l user -d "Print effective user ID"
|
complete id -s u -l user -d "Print effective user ID"
|
||||||
@@ -24,7 +24,7 @@ else
|
|||||||
complete id -s F -d "Print full name of the user"
|
complete id -s F -d "Print full name of the user"
|
||||||
complete id -s G -d "Print all group ids"
|
complete id -s G -d "Print all group ids"
|
||||||
complete id -s P -d "Print as passwd file entry"
|
complete id -s P -d "Print as passwd file entry"
|
||||||
complete id -s g -d "Print effective group id"
|
complete id -s g -d "Print effective group ID"
|
||||||
complete id -s n -d "Print name, not number"
|
complete id -s n -d "Print name, not number"
|
||||||
complete id -s p -d "Human-readable output"
|
complete id -s p -d "Human-readable output"
|
||||||
complete id -s r -d "Print real ID, not effective"
|
complete id -s r -d "Print real ID, not effective"
|
||||||
|
|||||||
@@ -548,8 +548,8 @@ function __fish_complete_ip
|
|||||||
attach "Attach process to network namespace" \
|
attach "Attach process to network namespace" \
|
||||||
delete "Delete network namespace" \
|
delete "Delete network namespace" \
|
||||||
set "Change network namespace attributes" \
|
set "Change network namespace attributes" \
|
||||||
identify "Display network namespace for a process id" \
|
identify "Display network namespace for a process ID" \
|
||||||
pids "Display process ids of processes running in network namespace" \
|
pids "Display process IDs of processes running in network namespace" \
|
||||||
monitor "Report as network namespace names are added and deleted" \
|
monitor "Report as network namespace names are added and deleted" \
|
||||||
exec "Execute command in network namespace" \
|
exec "Execute command in network namespace" \
|
||||||
help "Display help" \
|
help "Display help" \
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
complete -c jobs -s h -l help -d 'Display help and exit'
|
complete -c jobs -s h -l help -d 'Display help and exit'
|
||||||
complete -c jobs -s p -l pid -d "Show the process id of each process in the job"
|
complete -c jobs -s p -l pid -d "Show the process ID of each process in the job"
|
||||||
complete -c jobs -s g -l group -d "Show group id of job"
|
complete -c jobs -s g -l group -d "Show group ID of job"
|
||||||
complete -c jobs -s c -l command -d "Show commandname of each job"
|
complete -c jobs -s c -l command -d "Show commandname of each job"
|
||||||
complete -c jobs -s l -l last -d "Only show status for last job to be started"
|
complete -c jobs -s l -l last -d "Only show status for last job to be started"
|
||||||
complete -c jobs -s q -l quiet -l query -d "Check if a job exists without output"
|
complete -c jobs -s q -l quiet -l query -d "Check if a job exists without output"
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ set -l maybe_filter_private_vars '
|
|||||||
)'
|
)'
|
||||||
# We do not *filter* these by the given scope because you might want to set e.g. a global to shadow a universal.
|
# We do not *filter* these by the given scope because you might want to set e.g. a global to shadow a universal.
|
||||||
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(set -U | $maybe_filter_private_vars | string replace ' ' \t'Universal Variable: ')"
|
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(set -U | $maybe_filter_private_vars | string replace ' ' \t'Universal Variable: ')"
|
||||||
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(set -g | $maybe_filter_private_vars | string replace -r '^((?:history|fish_killring) ).*' '$1' | string replace ' ' \t'Global Variable: ')"
|
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(set -g | $maybe_filter_private_vars | string replace -r '^((?:history|fish_killring) ).*' '\$1' | string replace ' ' \t'Global Variable: ')"
|
||||||
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(set -l | $maybe_filter_private_vars | string replace ' ' \t'Local Variable: ')"
|
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(set -l | $maybe_filter_private_vars | string replace ' ' \t'Local Variable: ')"
|
||||||
# Complete some fish configuration variables even if they aren't set.
|
# Complete some fish configuration variables even if they aren't set.
|
||||||
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(__fish_complete_special_vars)"
|
complete -c set -n '__fish_is_nth_token 1; and not __fish_seen_argument -s e -l erase' -x -a "(__fish_complete_special_vars)"
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ function __fish_cache_put
|
|||||||
set -l dir (path dirname $cache_file)
|
set -l dir (path dirname $cache_file)
|
||||||
chown --reference=$dir $cache_file 2>/dev/null ||
|
chown --reference=$dir $cache_file 2>/dev/null ||
|
||||||
chown (
|
chown (
|
||||||
stat --format '%u:%g' $dir 2>/dev/null ||
|
if stat --version 2>&1 | string match -q 'BusyBox*'
|
||||||
stat -f '%u:%g' $dir
|
stat -c '%u:%g' $dir
|
||||||
|
else
|
||||||
|
stat --format '%u:%g' $dir 2>/dev/null ||
|
||||||
|
stat -f '%u:%g' $dir
|
||||||
|
end
|
||||||
) $cache_file
|
) $cache_file
|
||||||
end
|
end
|
||||||
|
|||||||
16
share/functions/__fish_list_files.fish
Normal file
16
share/functions/__fish_list_files.fish
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# localization: skip(private)
|
||||||
|
function __fish_list_files
|
||||||
|
set -l dir $argv[1]
|
||||||
|
if set -q __fish_data_dir[1]
|
||||||
|
# Construct a directory prefix without trailing slash.
|
||||||
|
if test -n "$dir"
|
||||||
|
set dir $__fish_data_dir/$dir
|
||||||
|
else
|
||||||
|
set dir $__fish_data_dir
|
||||||
|
end
|
||||||
|
set -l files $dir/**
|
||||||
|
string replace -- $dir/ '' $files
|
||||||
|
else
|
||||||
|
status list-files $dir
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
# localization: skip(private)
|
# localization: skip(private)
|
||||||
function __fish_print_commands --description "Print a list of documented fish commands"
|
function __fish_print_commands --description "Print a list of documented fish commands"
|
||||||
if set -q __fish_data_dir[1] && test -d $__fish_data_dir/man/man1/
|
if set -q __fish_data_dir[1] && test -d $__fish_data_dir/man/man1/
|
||||||
for file in $__fish_data_dir/man/man1/**.1*
|
printf %s\n $__fish_data_dir/man/man1/**.1*
|
||||||
string replace -r '.*/' '' -- $file |
|
else
|
||||||
string replace -r '.1(.gz)?$' '' |
|
status list-files man/man1/ 2>/dev/null
|
||||||
string match -rv '^fish-(?:changelog|completions|doc|tutorial|faq|for-bash-users|interactive|language|releasenotes|terminal-compatibility)$'
|
end |
|
||||||
end
|
string replace -r '.*/' '' |
|
||||||
end
|
|
||||||
status list-files man/man1/ 2>/dev/null |
|
|
||||||
string replace -r '.*/' '' -- $file |
|
|
||||||
string replace -r '.1(.gz)?$' '' |
|
string replace -r '.1(.gz)?$' '' |
|
||||||
string match -rv '^fish-(?:changelog|completions|doc|tutorial|faq|for-bash-users|interactive|language|releasenotes)$'
|
string match -rv '^fish-(?:changelog|completions|doc|tutorial|faq|for-bash-users|interactive|language|releasenotes|terminal-compatibility)$'
|
||||||
end
|
end
|
||||||
|
|||||||
10
share/functions/__fish_with_file.fish
Normal file
10
share/functions/__fish_with_file.fish
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# localization: skip(private)
|
||||||
|
function __fish_with_file
|
||||||
|
set -l file $argv[1]
|
||||||
|
set -l cmd $argv[2..]
|
||||||
|
if set -q __fish_data_dir[1]
|
||||||
|
$cmd $__fish_data_dir/$file
|
||||||
|
else
|
||||||
|
status get-file $file | $cmd
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# localization: tier3
|
# localization: tier3
|
||||||
function __ssh_history_completions -d "Retrieve `user@host` entries from history"
|
function __ssh_history_completions -d "Retrieve `user@host` entries from history"
|
||||||
history --prefix ssh --max=100 | string replace -rf '.* ([A-Za-z0-9._:-]+@[A-Za-z0-9._:-]+).*' '$1'
|
# Accept the typical hostname/ip chars, but no ":" at the end
|
||||||
|
history --prefix ssh --max=100 | string replace -rf '.* ([A-Za-z0-9._:-]+@[A-Za-z0-9._:-]*[A-Za-z0-9._-]).*' '$1'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ function fish_add_path --description "Add paths to the PATH"
|
|||||||
else
|
else
|
||||||
if set -q _flag_verbose
|
if set -q _flag_verbose
|
||||||
# print a message in verbose mode
|
# print a message in verbose mode
|
||||||
printf (_ "No paths to add, not setting anything.\n") "$p"
|
printf (_ "No paths to add, not setting anything.\n")
|
||||||
end
|
end
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ function fish_config --description "Launch fish's web based configuration"
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
set -l prompt_dir $__fish_data_dir/sample_prompts $__fish_data_dir/tools/web_config/sample_prompts
|
set -l prompt_dir $__fish_data_dir/tools/web_config/sample_prompts
|
||||||
switch $cmd
|
switch $cmd
|
||||||
case show
|
case show
|
||||||
set -l fish (status fish-path)
|
set -l fish (status fish-path)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ function funced --description 'Edit function definition'
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not set -q argv[1]
|
if not set -q argv[1]
|
||||||
printf (_ "%ls: Expected at least %d args, got only %d\n") funced 1 0
|
printf (_ "%s: Expected at least %d args, got only %d\n") funced 1 0
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function funcsave --description "Save the current definition of all specified fu
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not set -q argv[1]
|
if not set -q argv[1]
|
||||||
printf (_ "%ls: Expected at least %d args, got only %d\n") funcsave 1 0 >&2
|
printf (_ "%s: Expected at least %d args, got only %d\n") funcsave 1 0 >&2
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -185,11 +185,11 @@ function history --description "display or manipulate interactive command histor
|
|||||||
case clear # clear the interactive command history
|
case clear # clear the interactive command history
|
||||||
if test -n "$search_mode"
|
if test -n "$search_mode"
|
||||||
or set -q show_time[1]
|
or set -q show_time[1]
|
||||||
printf (_ "%ls: %ls: subcommand takes no options\n") history $hist_cmd >&2
|
printf (_ "%s: %s: subcommand takes no options\n") history $hist_cmd >&2
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
if set -q argv[1]
|
if set -q argv[1]
|
||||||
printf (_ "%ls: %ls: expected %d arguments; got %d\n") history $hist_cmd 0 (count $argv) >&2
|
printf (_ "%s: %s: expected %d arguments; got %d\n") history $hist_cmd 0 (count $argv) >&2
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -214,7 +214,7 @@ function history --description "display or manipulate interactive command histor
|
|||||||
|
|
||||||
builtin history append $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $newitem
|
builtin history append $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $newitem
|
||||||
case '*'
|
case '*'
|
||||||
printf "%ls: unexpected subcommand '%ls'\n" $cmd $hist_cmd
|
printf "%s: unexpected subcommand '%s'\n" $cmd $hist_cmd
|
||||||
return 2
|
return 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ if not command -sq open
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not set -q argv[1]
|
if not set -q argv[1]
|
||||||
printf (_ "%ls: Expected at least %d args, got only %d\n") open 1 0 >&2
|
printf (_ "%s: Expected at least %d args, got only %d\n") open 1 0 >&2
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ from __future__ import print_function
|
|||||||
from deroff import Deroffer
|
from deroff import Deroffer
|
||||||
import argparse
|
import argparse
|
||||||
import bz2
|
import bz2
|
||||||
import codecs
|
|
||||||
import errno
|
import errno
|
||||||
import gzip
|
import gzip
|
||||||
import os
|
import os
|
||||||
@@ -734,7 +733,7 @@ class TypeDeroffManParser(ManParser):
|
|||||||
# Raises IOError if it cannot be opened
|
# Raises IOError if it cannot be opened
|
||||||
def file_is_overwritable(path):
|
def file_is_overwritable(path):
|
||||||
result = False
|
result = False
|
||||||
file = codecs.open(path, "r", encoding="utf-8")
|
file = open(path, "r", encoding="utf-8")
|
||||||
for line in file:
|
for line in file:
|
||||||
# Skip leading empty lines
|
# Skip leading empty lines
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
@@ -893,7 +892,7 @@ def parse_manpage_at_path(manpage_path, output_directory):
|
|||||||
else:
|
else:
|
||||||
fullpath = os.path.join(output_directory, CMDNAME + ".fish")
|
fullpath = os.path.join(output_directory, CMDNAME + ".fish")
|
||||||
try:
|
try:
|
||||||
output_file = codecs.open(fullpath, "w", encoding="utf-8")
|
output_file = open(fullpath, "w", encoding="utf-8")
|
||||||
except IOError as err:
|
except IOError as err:
|
||||||
add_diagnostic(
|
add_diagnostic(
|
||||||
"Unable to open file '%s': error(%d): %s"
|
"Unable to open file '%s': error(%d): %s"
|
||||||
|
|||||||
@@ -68,14 +68,6 @@ def find_executable(exe, paths=()):
|
|||||||
return proposed_path
|
return proposed_path
|
||||||
|
|
||||||
|
|
||||||
def isMacOS10_12_5_OrLater():
|
|
||||||
"""Return whether this system is macOS 10.12.5 or a later version."""
|
|
||||||
try:
|
|
||||||
return [int(x) for x in platform.mac_ver()[0].split(".")] >= [10, 12, 5]
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_wsl():
|
def is_wsl():
|
||||||
"""Return whether we are running under the Windows Subsystem for Linux"""
|
"""Return whether we are running under the Windows Subsystem for Linux"""
|
||||||
if "linux" in platform.system().lower() and os.access("/proc/version", os.R_OK):
|
if "linux" in platform.system().lower() and os.access("/proc/version", os.R_OK):
|
||||||
@@ -1423,7 +1415,7 @@ fish_bin_path = None
|
|||||||
fish_bin_name = "fish.exe" if is_windows() else "fish"
|
fish_bin_name = "fish.exe" if is_windows() else "fish"
|
||||||
|
|
||||||
if not fish_bin_dir:
|
if not fish_bin_dir:
|
||||||
print("The $__fish_bin_dir environment variable is not set. " "Looking in $PATH...")
|
print("The $__fish_bin_dir environment variable is not set. Looking in $PATH...")
|
||||||
fish_bin_path = find_executable(fish_bin_name)
|
fish_bin_path = find_executable(fish_bin_name)
|
||||||
if not fish_bin_path:
|
if not fish_bin_path:
|
||||||
print("fish could not be found. Is fish installed correctly?")
|
print("fish could not be found. Is fish installed correctly?")
|
||||||
@@ -1544,8 +1536,9 @@ print("%sHit ENTER to stop.%s" % (ENTER_BOLD_MODE, EXIT_ATTRIBUTE_MODE))
|
|||||||
|
|
||||||
|
|
||||||
def runThing():
|
def runThing():
|
||||||
if isMacOS10_12_5_OrLater():
|
if os.environ.get("BROWSER") == "true":
|
||||||
subprocess.check_call(["open", fileurl])
|
# Don't start a browser in this case (see issue #11926)
|
||||||
|
pass
|
||||||
elif is_wsl():
|
elif is_wsl():
|
||||||
cmd_path = find_executable("cmd.exe", COMMON_WSL_CMD_PATHS)
|
cmd_path = find_executable("cmd.exe", COMMON_WSL_CMD_PATHS)
|
||||||
if cmd_path:
|
if cmd_path:
|
||||||
@@ -1606,7 +1599,15 @@ def capture_enter(port):
|
|||||||
|
|
||||||
def get_windows_signal():
|
def get_windows_signal():
|
||||||
"""Using socket as a replacement for stdin on Windows."""
|
"""Using socket as a replacement for stdin on Windows."""
|
||||||
(sig, sig_port) = create_socket(8000, 9000)
|
# The intent is to get a free port between 8000 and 9000, like for the HTTP
|
||||||
|
# server. But we already know that port 8000..PORT are not available. So
|
||||||
|
# starting from `PORT+1` is more efficient.
|
||||||
|
# More importantly though, Windows allows multiple sockets to bind to the
|
||||||
|
# same port in some circumstances (see SO_EXCLUSIVEADDRUSE documentation),
|
||||||
|
# and thus allows the signal socket to bind to the same port as HTTP.
|
||||||
|
# A browser may then end up reaching the wrong socket and causing
|
||||||
|
# fish_config to shutdown prematurely.
|
||||||
|
(sig, sig_port) = create_socket(PORT + 1, 9000)
|
||||||
threading.Thread(target=capture_enter, args=(sig_port,)).start()
|
threading.Thread(target=capture_enter, args=(sig_port,)).start()
|
||||||
return sig
|
return sig
|
||||||
|
|
||||||
|
|||||||
224
src/ast.rs
224
src/ast.rs
@@ -24,6 +24,7 @@
|
|||||||
TOK_ACCEPT_UNFINISHED, TOK_ARGUMENT_LIST, TOK_CONTINUE_AFTER_ERROR, TOK_SHOW_COMMENTS,
|
TOK_ACCEPT_UNFINISHED, TOK_ARGUMENT_LIST, TOK_CONTINUE_AFTER_ERROR, TOK_SHOW_COMMENTS,
|
||||||
};
|
};
|
||||||
use crate::wchar::prelude::*;
|
use crate::wchar::prelude::*;
|
||||||
|
use macro_rules_attribute::derive;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::convert::AsMut;
|
use std::convert::AsMut;
|
||||||
use std::ops::{ControlFlow, Deref};
|
use std::ops::{ControlFlow, Deref};
|
||||||
@@ -162,10 +163,10 @@ fn describe(&self) -> WString {
|
|||||||
let mut res = ast_kind_to_string(self.kind()).to_owned();
|
let mut res = ast_kind_to_string(self.kind()).to_owned();
|
||||||
if let Some(n) = self.as_token() {
|
if let Some(n) = self.as_token() {
|
||||||
let token_type = n.token_type().to_wstr();
|
let token_type = n.token_type().to_wstr();
|
||||||
sprintf!(=> &mut res, " '%ls'", token_type);
|
sprintf!(=> &mut res, " '%s'", token_type);
|
||||||
} else if let Some(n) = self.as_keyword() {
|
} else if let Some(n) = self.as_keyword() {
|
||||||
let keyword = n.keyword().to_wstr();
|
let keyword = n.keyword().to_wstr();
|
||||||
sprintf!(=> &mut res, " '%ls'", keyword);
|
sprintf!(=> &mut res, " '%s'", keyword);
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -404,8 +405,8 @@ trait CheckParse: Default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Implement the node trait.
|
/// Implement the node trait.
|
||||||
macro_rules! implement_node {
|
macro_rules! Node {
|
||||||
( $name:ident ) => {
|
($name:ident) => {
|
||||||
impl Node for $name {
|
impl Node for $name {
|
||||||
fn kind(&self) -> Kind<'_> {
|
fn kind(&self) -> Kind<'_> {
|
||||||
Kind::$name(self)
|
Kind::$name(self)
|
||||||
@@ -425,11 +426,19 @@ fn cast(node: &dyn Node) -> Option<&Self> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
( $(#[$_m:meta])* $_v:vis struct $name:ident $_:tt $(;)? ) => {
|
||||||
|
Node!($name);
|
||||||
|
};
|
||||||
|
|
||||||
|
( $(#[$_m:meta])* $_v:vis enum $name:ident $_:tt ) => {
|
||||||
|
Node!($name);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement the leaf trait.
|
/// Implement the leaf trait.
|
||||||
macro_rules! implement_leaf {
|
macro_rules! Leaf {
|
||||||
( $name:ident ) => {
|
($name:ident) => {
|
||||||
impl Leaf for $name {
|
impl Leaf for $name {
|
||||||
fn range(&self) -> Option<SourceRange> {
|
fn range(&self) -> Option<SourceRange> {
|
||||||
self.range
|
self.range
|
||||||
@@ -450,17 +459,20 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
( $(#[$_m:meta])* $_v:vis struct $name:ident $_:tt $(;)? ) => {
|
||||||
|
Leaf!($name);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a node that implements the keyword trait.
|
/// Define a node that implements the keyword trait.
|
||||||
macro_rules! define_keyword_node {
|
macro_rules! define_keyword_node {
|
||||||
( $name:ident, $($allowed:ident),* $(,)? ) => {
|
( $name:ident, $($allowed:ident),* $(,)? ) => {
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Leaf!)]
|
||||||
pub struct $name {
|
pub struct $name {
|
||||||
range: Option<SourceRange>,
|
range: Option<SourceRange>,
|
||||||
keyword: ParseKeyword,
|
keyword: ParseKeyword,
|
||||||
}
|
}
|
||||||
implement_leaf!($name);
|
|
||||||
impl Node for $name {
|
impl Node for $name {
|
||||||
fn kind(&self) -> Kind<'_> {
|
fn kind(&self) -> Kind<'_> {
|
||||||
Kind::Keyword(self)
|
Kind::Keyword(self)
|
||||||
@@ -489,7 +501,7 @@ fn as_leaf(&self) -> &dyn Leaf {
|
|||||||
/// Define a node that implements the token trait.
|
/// Define a node that implements the token trait.
|
||||||
macro_rules! define_token_node {
|
macro_rules! define_token_node {
|
||||||
( $name:ident, $($allowed:ident),* $(,)? ) => {
|
( $name:ident, $($allowed:ident),* $(,)? ) => {
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Leaf!)]
|
||||||
pub struct $name {
|
pub struct $name {
|
||||||
range: Option<SourceRange>,
|
range: Option<SourceRange>,
|
||||||
parse_token_type: ParseTokenType,
|
parse_token_type: ParseTokenType,
|
||||||
@@ -502,7 +514,6 @@ fn kind_mut(&mut self) -> KindMut<'_> {
|
|||||||
KindMut::Token(self)
|
KindMut::Token(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
implement_leaf!($name);
|
|
||||||
impl Token for $name {
|
impl Token for $name {
|
||||||
fn token_type(&self) -> ParseTokenType {
|
fn token_type(&self) -> ParseTokenType {
|
||||||
self.parse_token_type
|
self.parse_token_type
|
||||||
@@ -535,11 +546,9 @@ macro_rules! define_list_node {
|
|||||||
$name:ident,
|
$name:ident,
|
||||||
$contents:ident
|
$contents:ident
|
||||||
) => {
|
) => {
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!)]
|
||||||
pub struct $name(Box<[$contents]>);
|
pub struct $name(Box<[$contents]>);
|
||||||
|
|
||||||
implement_node!($name);
|
|
||||||
|
|
||||||
impl Deref for $name {
|
impl Deref for $name {
|
||||||
type Target = Box<[$contents]>;
|
type Target = Box<[$contents]>;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@@ -582,11 +591,15 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Implement the acceptor trait for the given branch node.
|
/// Implement the acceptor trait for the given branch node.
|
||||||
macro_rules! implement_acceptor_for_branch {
|
macro_rules! Acceptor {
|
||||||
(
|
(
|
||||||
$name:ident
|
$(#[$_m:meta])*
|
||||||
$(, $field_name:ident )*
|
$_v:vis struct $name:ident {
|
||||||
$(,)?
|
$(
|
||||||
|
$(#[$_fm:meta])*
|
||||||
|
$_fv:vis $field_name:ident : $_ft:ty
|
||||||
|
),* $(,)?
|
||||||
|
}
|
||||||
) => {
|
) => {
|
||||||
impl Acceptor for $name {
|
impl Acceptor for $name {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
@@ -612,18 +625,16 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
|||||||
visitor.did_visit_fields_of(self, flow);
|
visitor.did_visit_fields_of(self, flow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A redirection has an operator like > or 2>, and a target like /dev/null or &1.
|
/// A redirection has an operator like > or 2>, and a target like /dev/null or &1.
|
||||||
/// Note that pipes are not redirections.
|
/// Note that pipes are not redirections.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct Redirection {
|
pub struct Redirection {
|
||||||
pub oper: TokenRedirection,
|
pub oper: TokenRedirection,
|
||||||
pub target: String_,
|
pub target: String_,
|
||||||
}
|
}
|
||||||
implement_node!(Redirection);
|
|
||||||
implement_acceptor_for_branch!(Redirection, oper, target);
|
|
||||||
|
|
||||||
impl CheckParse for Redirection {
|
impl CheckParse for Redirection {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
@@ -633,7 +644,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
|
|
||||||
define_list_node!(VariableAssignmentList, VariableAssignment);
|
define_list_node!(VariableAssignmentList, VariableAssignment);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Node!)]
|
||||||
pub enum ArgumentOrRedirection {
|
pub enum ArgumentOrRedirection {
|
||||||
Argument(Argument),
|
Argument(Argument),
|
||||||
Redirection(Box<Redirection>), // Boxed because it's bigger
|
Redirection(Box<Redirection>), // Boxed because it's bigger
|
||||||
@@ -689,8 +700,6 @@ pub fn redirection(&self) -> &Redirection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implement_node!(ArgumentOrRedirection);
|
|
||||||
|
|
||||||
impl CheckParse for ArgumentOrRedirection {
|
impl CheckParse for ArgumentOrRedirection {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
let typ = pop.peek_type(0);
|
let typ = pop.peek_type(0);
|
||||||
@@ -701,7 +710,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
define_list_node!(ArgumentOrRedirectionList, ArgumentOrRedirection);
|
define_list_node!(ArgumentOrRedirectionList, ArgumentOrRedirection);
|
||||||
|
|
||||||
/// A statement is a normal command, or an if / while / etc
|
/// A statement is a normal command, or an if / while / etc
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Node!)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Decorated(DecoratedStatement),
|
Decorated(DecoratedStatement),
|
||||||
Not(Box<NotStatement>),
|
Not(Box<NotStatement>),
|
||||||
@@ -710,7 +719,6 @@ pub enum Statement {
|
|||||||
If(Box<IfStatement>),
|
If(Box<IfStatement>),
|
||||||
Switch(Box<SwitchStatement>),
|
Switch(Box<SwitchStatement>),
|
||||||
}
|
}
|
||||||
implement_node!(Statement);
|
|
||||||
|
|
||||||
impl Default for Statement {
|
impl Default for Statement {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@@ -756,7 +764,7 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
|||||||
|
|
||||||
/// A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases
|
/// A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases
|
||||||
/// like if statements, where we require a command).
|
/// like if statements, where we require a command).
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct JobPipeline {
|
pub struct JobPipeline {
|
||||||
/// Maybe the time keyword.
|
/// Maybe the time keyword.
|
||||||
pub time: Option<KeywordTime>,
|
pub time: Option<KeywordTime>,
|
||||||
@@ -769,11 +777,9 @@ pub struct JobPipeline {
|
|||||||
/// Maybe backgrounded.
|
/// Maybe backgrounded.
|
||||||
pub bg: Option<TokenBackground>,
|
pub bg: Option<TokenBackground>,
|
||||||
}
|
}
|
||||||
implement_node!(JobPipeline);
|
|
||||||
implement_acceptor_for_branch!(JobPipeline, time, variables, statement, continuation, bg);
|
|
||||||
|
|
||||||
/// A job_conjunction is a job followed by a && or || continuations.
|
/// A job_conjunction is a job followed by a && or || continuations.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct JobConjunction {
|
pub struct JobConjunction {
|
||||||
/// The job conjunction decorator.
|
/// The job conjunction decorator.
|
||||||
pub decorator: Option<JobConjunctionDecorator>,
|
pub decorator: Option<JobConjunctionDecorator>,
|
||||||
@@ -786,8 +792,6 @@ pub struct JobConjunction {
|
|||||||
/// only fail to be present if we ran out of tokens.
|
/// only fail to be present if we ran out of tokens.
|
||||||
pub semi_nl: Option<SemiNl>,
|
pub semi_nl: Option<SemiNl>,
|
||||||
}
|
}
|
||||||
implement_node!(JobConjunction);
|
|
||||||
implement_acceptor_for_branch!(JobConjunction, decorator, job, continuations, semi_nl);
|
|
||||||
|
|
||||||
impl CheckParse for JobConjunction {
|
impl CheckParse for JobConjunction {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
@@ -802,7 +806,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct ForHeader {
|
pub struct ForHeader {
|
||||||
/// 'for'
|
/// 'for'
|
||||||
pub kw_for: KeywordFor,
|
pub kw_for: KeywordFor,
|
||||||
@@ -815,20 +819,16 @@ pub struct ForHeader {
|
|||||||
/// newline or semicolon
|
/// newline or semicolon
|
||||||
pub semi_nl: SemiNl,
|
pub semi_nl: SemiNl,
|
||||||
}
|
}
|
||||||
implement_node!(ForHeader);
|
|
||||||
implement_acceptor_for_branch!(ForHeader, kw_for, var_name, kw_in, args, semi_nl);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct WhileHeader {
|
pub struct WhileHeader {
|
||||||
/// 'while'
|
/// 'while'
|
||||||
pub kw_while: KeywordWhile,
|
pub kw_while: KeywordWhile,
|
||||||
pub condition: JobConjunction,
|
pub condition: JobConjunction,
|
||||||
pub andor_tail: AndorJobList,
|
pub andor_tail: AndorJobList,
|
||||||
}
|
}
|
||||||
implement_node!(WhileHeader);
|
|
||||||
implement_acceptor_for_branch!(WhileHeader, kw_while, condition, andor_tail);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct FunctionHeader {
|
pub struct FunctionHeader {
|
||||||
pub kw_function: KeywordFunction,
|
pub kw_function: KeywordFunction,
|
||||||
/// functions require at least one argument.
|
/// functions require at least one argument.
|
||||||
@@ -836,20 +836,16 @@ pub struct FunctionHeader {
|
|||||||
pub args: ArgumentList,
|
pub args: ArgumentList,
|
||||||
pub semi_nl: SemiNl,
|
pub semi_nl: SemiNl,
|
||||||
}
|
}
|
||||||
implement_node!(FunctionHeader);
|
|
||||||
implement_acceptor_for_branch!(FunctionHeader, kw_function, first_arg, args, semi_nl);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct BeginHeader {
|
pub struct BeginHeader {
|
||||||
pub kw_begin: KeywordBegin,
|
pub kw_begin: KeywordBegin,
|
||||||
/// Note that 'begin' does NOT require a semi or nl afterwards.
|
/// Note that 'begin' does NOT require a semi or nl afterwards.
|
||||||
/// This is valid: begin echo hi; end
|
/// This is valid: begin echo hi; end
|
||||||
pub semi_nl: Option<SemiNl>,
|
pub semi_nl: Option<SemiNl>,
|
||||||
}
|
}
|
||||||
implement_node!(BeginHeader);
|
|
||||||
implement_acceptor_for_branch!(BeginHeader, kw_begin, semi_nl);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct BlockStatement {
|
pub struct BlockStatement {
|
||||||
/// A header like for, while, etc.
|
/// A header like for, while, etc.
|
||||||
pub header: BlockStatementHeader,
|
pub header: BlockStatementHeader,
|
||||||
@@ -860,10 +856,8 @@ pub struct BlockStatement {
|
|||||||
/// Arguments and redirections associated with the block.
|
/// Arguments and redirections associated with the block.
|
||||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||||
}
|
}
|
||||||
implement_node!(BlockStatement);
|
|
||||||
implement_acceptor_for_branch!(BlockStatement, header, jobs, end, args_or_redirs);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct BraceStatement {
|
pub struct BraceStatement {
|
||||||
/// The opening brace, in command position.
|
/// The opening brace, in command position.
|
||||||
pub left_brace: TokenLeftBrace,
|
pub left_brace: TokenLeftBrace,
|
||||||
@@ -874,16 +868,8 @@ pub struct BraceStatement {
|
|||||||
/// Arguments and redirections associated with the block.
|
/// Arguments and redirections associated with the block.
|
||||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||||
}
|
}
|
||||||
implement_node!(BraceStatement);
|
|
||||||
implement_acceptor_for_branch!(
|
|
||||||
BraceStatement,
|
|
||||||
left_brace,
|
|
||||||
jobs,
|
|
||||||
right_brace,
|
|
||||||
args_or_redirs
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct IfClause {
|
pub struct IfClause {
|
||||||
/// The 'if' keyword.
|
/// The 'if' keyword.
|
||||||
pub kw_if: KeywordIf,
|
pub kw_if: KeywordIf,
|
||||||
@@ -894,18 +880,14 @@ pub struct IfClause {
|
|||||||
/// The body to execute if the condition is true.
|
/// The body to execute if the condition is true.
|
||||||
pub body: JobList,
|
pub body: JobList,
|
||||||
}
|
}
|
||||||
implement_node!(IfClause);
|
|
||||||
implement_acceptor_for_branch!(IfClause, kw_if, condition, andor_tail, body);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct ElseifClause {
|
pub struct ElseifClause {
|
||||||
/// The 'else' keyword.
|
/// The 'else' keyword.
|
||||||
pub kw_else: KeywordElse,
|
pub kw_else: KeywordElse,
|
||||||
/// The 'if' clause following it.
|
/// The 'if' clause following it.
|
||||||
pub if_clause: IfClause,
|
pub if_clause: IfClause,
|
||||||
}
|
}
|
||||||
implement_node!(ElseifClause);
|
|
||||||
implement_acceptor_for_branch!(ElseifClause, kw_else, if_clause);
|
|
||||||
impl CheckParse for ElseifClause {
|
impl CheckParse for ElseifClause {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
pop.peek_token(0).keyword == ParseKeyword::Else
|
pop.peek_token(0).keyword == ParseKeyword::Else
|
||||||
@@ -915,22 +897,20 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
|
|
||||||
define_list_node!(ElseifClauseList, ElseifClause);
|
define_list_node!(ElseifClauseList, ElseifClause);
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct ElseClause {
|
pub struct ElseClause {
|
||||||
/// else ; body
|
/// else ; body
|
||||||
pub kw_else: KeywordElse,
|
pub kw_else: KeywordElse,
|
||||||
pub semi_nl: Option<SemiNl>,
|
pub semi_nl: Option<SemiNl>,
|
||||||
pub body: JobList,
|
pub body: JobList,
|
||||||
}
|
}
|
||||||
implement_node!(ElseClause);
|
|
||||||
implement_acceptor_for_branch!(ElseClause, kw_else, semi_nl, body);
|
|
||||||
impl CheckParse for ElseClause {
|
impl CheckParse for ElseClause {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
pop.peek_token(0).keyword == ParseKeyword::Else
|
pop.peek_token(0).keyword == ParseKeyword::Else
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct IfStatement {
|
pub struct IfStatement {
|
||||||
/// if part
|
/// if part
|
||||||
pub if_clause: IfClause,
|
pub if_clause: IfClause,
|
||||||
@@ -943,17 +923,8 @@ pub struct IfStatement {
|
|||||||
/// block args / redirs
|
/// block args / redirs
|
||||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||||
}
|
}
|
||||||
implement_node!(IfStatement);
|
|
||||||
implement_acceptor_for_branch!(
|
|
||||||
IfStatement,
|
|
||||||
if_clause,
|
|
||||||
elseif_clauses,
|
|
||||||
else_clause,
|
|
||||||
end,
|
|
||||||
args_or_redirs
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct CaseItem {
|
pub struct CaseItem {
|
||||||
/// case \<arguments\> ; body
|
/// case \<arguments\> ; body
|
||||||
pub kw_case: KeywordCase,
|
pub kw_case: KeywordCase,
|
||||||
@@ -961,15 +932,13 @@ pub struct CaseItem {
|
|||||||
pub semi_nl: SemiNl,
|
pub semi_nl: SemiNl,
|
||||||
pub body: JobList,
|
pub body: JobList,
|
||||||
}
|
}
|
||||||
implement_node!(CaseItem);
|
|
||||||
implement_acceptor_for_branch!(CaseItem, kw_case, arguments, semi_nl, body);
|
|
||||||
impl CheckParse for CaseItem {
|
impl CheckParse for CaseItem {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
pop.peek_token(0).keyword == ParseKeyword::Case
|
pop.peek_token(0).keyword == ParseKeyword::Case
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct SwitchStatement {
|
pub struct SwitchStatement {
|
||||||
/// switch \<argument\> ; body ; end args_redirs
|
/// switch \<argument\> ; body ; end args_redirs
|
||||||
pub kw_switch: KeywordSwitch,
|
pub kw_switch: KeywordSwitch,
|
||||||
@@ -979,20 +948,10 @@ pub struct SwitchStatement {
|
|||||||
pub end: KeywordEnd,
|
pub end: KeywordEnd,
|
||||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||||
}
|
}
|
||||||
implement_node!(SwitchStatement);
|
|
||||||
implement_acceptor_for_branch!(
|
|
||||||
SwitchStatement,
|
|
||||||
kw_switch,
|
|
||||||
argument,
|
|
||||||
semi_nl,
|
|
||||||
cases,
|
|
||||||
end,
|
|
||||||
args_or_redirs
|
|
||||||
);
|
|
||||||
|
|
||||||
/// A decorated_statement is a command with a list of arguments_or_redirections, possibly with
|
/// A decorated_statement is a command with a list of arguments_or_redirections, possibly with
|
||||||
/// "builtin" or "command" or "exec"
|
/// "builtin" or "command" or "exec"
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct DecoratedStatement {
|
pub struct DecoratedStatement {
|
||||||
/// An optional decoration (command, builtin, exec, etc).
|
/// An optional decoration (command, builtin, exec, etc).
|
||||||
pub opt_decoration: Option<DecoratedStatementDecorator>,
|
pub opt_decoration: Option<DecoratedStatementDecorator>,
|
||||||
@@ -1001,11 +960,9 @@ pub struct DecoratedStatement {
|
|||||||
/// Args and redirs
|
/// Args and redirs
|
||||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||||
}
|
}
|
||||||
implement_node!(DecoratedStatement);
|
|
||||||
implement_acceptor_for_branch!(DecoratedStatement, opt_decoration, command, args_or_redirs);
|
|
||||||
|
|
||||||
/// A not statement like `not true` or `! true`
|
/// A not statement like `not true` or `! true`
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct NotStatement {
|
pub struct NotStatement {
|
||||||
/// Keyword, either not or exclam.
|
/// Keyword, either not or exclam.
|
||||||
pub kw: KeywordNot,
|
pub kw: KeywordNot,
|
||||||
@@ -1013,18 +970,14 @@ pub struct NotStatement {
|
|||||||
pub variables: VariableAssignmentList,
|
pub variables: VariableAssignmentList,
|
||||||
pub contents: Statement,
|
pub contents: Statement,
|
||||||
}
|
}
|
||||||
implement_node!(NotStatement);
|
|
||||||
implement_acceptor_for_branch!(NotStatement, kw, time, variables, contents);
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct JobContinuation {
|
pub struct JobContinuation {
|
||||||
pub pipe: TokenPipe,
|
pub pipe: TokenPipe,
|
||||||
pub newlines: MaybeNewlines,
|
pub newlines: MaybeNewlines,
|
||||||
pub variables: VariableAssignmentList,
|
pub variables: VariableAssignmentList,
|
||||||
pub statement: Statement,
|
pub statement: Statement,
|
||||||
}
|
}
|
||||||
implement_node!(JobContinuation);
|
|
||||||
implement_acceptor_for_branch!(JobContinuation, pipe, newlines, variables, statement);
|
|
||||||
impl CheckParse for JobContinuation {
|
impl CheckParse for JobContinuation {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
pop.peek_type(0) == ParseTokenType::pipe
|
pop.peek_type(0) == ParseTokenType::pipe
|
||||||
@@ -1033,7 +986,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
|
|
||||||
define_list_node!(JobContinuationList, JobContinuation);
|
define_list_node!(JobContinuationList, JobContinuation);
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct JobConjunctionContinuation {
|
pub struct JobConjunctionContinuation {
|
||||||
/// The && or || token.
|
/// The && or || token.
|
||||||
pub conjunction: TokenConjunction,
|
pub conjunction: TokenConjunction,
|
||||||
@@ -1041,8 +994,6 @@ pub struct JobConjunctionContinuation {
|
|||||||
/// The job itself.
|
/// The job itself.
|
||||||
pub job: JobPipeline,
|
pub job: JobPipeline,
|
||||||
}
|
}
|
||||||
implement_node!(JobConjunctionContinuation);
|
|
||||||
implement_acceptor_for_branch!(JobConjunctionContinuation, conjunction, newlines, job);
|
|
||||||
impl CheckParse for JobConjunctionContinuation {
|
impl CheckParse for JobConjunctionContinuation {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
let typ = pop.peek_type(0);
|
let typ = pop.peek_type(0);
|
||||||
@@ -1053,12 +1004,10 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
/// An andor_job just wraps a job, but requires that the job have an 'and' or 'or' job_decorator.
|
/// An andor_job just wraps a job, but requires that the job have an 'and' or 'or' job_decorator.
|
||||||
/// Note this is only used for andor_job_list; jobs that are not part of an andor_job_list are not
|
/// Note this is only used for andor_job_list; jobs that are not part of an andor_job_list are not
|
||||||
/// instances of this.
|
/// instances of this.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct AndorJob {
|
pub struct AndorJob {
|
||||||
pub job: JobConjunction,
|
pub job: JobConjunction,
|
||||||
}
|
}
|
||||||
implement_node!(AndorJob);
|
|
||||||
implement_acceptor_for_branch!(AndorJob, job);
|
|
||||||
impl CheckParse for AndorJob {
|
impl CheckParse for AndorJob {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
let keyword = pop.peek_token(0).keyword;
|
let keyword = pop.peek_token(0).keyword;
|
||||||
@@ -1080,12 +1029,10 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
/// A freestanding_argument_list is equivalent to a normal argument list, except it may contain
|
/// A freestanding_argument_list is equivalent to a normal argument list, except it may contain
|
||||||
/// TOK_END (newlines, and even semicolons, for historical reasons).
|
/// TOK_END (newlines, and even semicolons, for historical reasons).
|
||||||
/// In practice the tok_ends are ignored by fish code so we do not bother to store them.
|
/// In practice the tok_ends are ignored by fish code so we do not bother to store them.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||||
pub struct FreestandingArgumentList {
|
pub struct FreestandingArgumentList {
|
||||||
pub arguments: ArgumentList,
|
pub arguments: ArgumentList,
|
||||||
}
|
}
|
||||||
implement_node!(FreestandingArgumentList);
|
|
||||||
implement_acceptor_for_branch!(FreestandingArgumentList, arguments);
|
|
||||||
|
|
||||||
define_list_node!(JobConjunctionContinuationList, JobConjunctionContinuation);
|
define_list_node!(JobConjunctionContinuationList, JobConjunctionContinuation);
|
||||||
|
|
||||||
@@ -1097,12 +1044,10 @@ pub struct FreestandingArgumentList {
|
|||||||
define_list_node!(CaseItemList, CaseItem);
|
define_list_node!(CaseItemList, CaseItem);
|
||||||
|
|
||||||
/// A variable_assignment contains a source range like FOO=bar.
|
/// A variable_assignment contains a source range like FOO=bar.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Leaf!)]
|
||||||
pub struct VariableAssignment {
|
pub struct VariableAssignment {
|
||||||
range: Option<SourceRange>,
|
range: Option<SourceRange>,
|
||||||
}
|
}
|
||||||
implement_node!(VariableAssignment);
|
|
||||||
implement_leaf!(VariableAssignment);
|
|
||||||
impl CheckParse for VariableAssignment {
|
impl CheckParse for VariableAssignment {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
// Do we have a variable assignment at all?
|
// Do we have a variable assignment at all?
|
||||||
@@ -1124,21 +1069,17 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Zero or more newlines.
|
/// Zero or more newlines.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Leaf!)]
|
||||||
pub struct MaybeNewlines {
|
pub struct MaybeNewlines {
|
||||||
range: Option<SourceRange>,
|
range: Option<SourceRange>,
|
||||||
}
|
}
|
||||||
implement_node!(MaybeNewlines);
|
|
||||||
implement_leaf!(MaybeNewlines);
|
|
||||||
|
|
||||||
/// An argument is just a node whose source range determines its contents.
|
/// An argument is just a node whose source range determines its contents.
|
||||||
/// This is a separate type because it is sometimes useful to find all arguments.
|
/// This is a separate type because it is sometimes useful to find all arguments.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug, Node!, Leaf!)]
|
||||||
pub struct Argument {
|
pub struct Argument {
|
||||||
range: Option<SourceRange>,
|
range: Option<SourceRange>,
|
||||||
}
|
}
|
||||||
implement_node!(Argument);
|
|
||||||
implement_leaf!(Argument);
|
|
||||||
impl CheckParse for Argument {
|
impl CheckParse for Argument {
|
||||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
pop.peek_type(0) == ParseTokenType::string
|
pop.peek_type(0) == ParseTokenType::string
|
||||||
@@ -1226,14 +1167,13 @@ pub fn decoration(&self) -> StatementDecoration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Node!)]
|
||||||
pub enum BlockStatementHeader {
|
pub enum BlockStatementHeader {
|
||||||
Begin(BeginHeader),
|
Begin(BeginHeader),
|
||||||
For(ForHeader),
|
For(ForHeader),
|
||||||
While(WhileHeader),
|
While(WhileHeader),
|
||||||
Function(FunctionHeader),
|
Function(FunctionHeader),
|
||||||
}
|
}
|
||||||
implement_node!(BlockStatementHeader);
|
|
||||||
|
|
||||||
impl Default for BlockStatementHeader {
|
impl Default for BlockStatementHeader {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@@ -1507,23 +1447,23 @@ pub fn dump(&self, orig: &wstr) -> WString {
|
|||||||
if let Kind::Argument(n) = node.kind() {
|
if let Kind::Argument(n) = node.kind() {
|
||||||
result += "argument";
|
result += "argument";
|
||||||
if let Some(argsrc) = n.try_source(orig) {
|
if let Some(argsrc) = n.try_source(orig) {
|
||||||
sprintf!(=> &mut result, ": '%ls'", argsrc);
|
sprintf!(=> &mut result, ": '%s'", argsrc);
|
||||||
}
|
}
|
||||||
} else if let Some(n) = node.as_keyword() {
|
} else if let Some(n) = node.as_keyword() {
|
||||||
sprintf!(=> &mut result, "keyword: %ls", n.keyword().to_wstr());
|
sprintf!(=> &mut result, "keyword: %s", n.keyword().to_wstr());
|
||||||
} else if let Some(n) = node.as_token() {
|
} else if let Some(n) = node.as_token() {
|
||||||
let desc = match n.token_type() {
|
let desc = match n.token_type() {
|
||||||
ParseTokenType::string => {
|
ParseTokenType::string => {
|
||||||
let mut desc = WString::from_str("string");
|
let mut desc = WString::from_str("string");
|
||||||
if let Some(strsource) = n.try_source(orig) {
|
if let Some(strsource) = n.try_source(orig) {
|
||||||
sprintf!(=> &mut desc, ": '%ls'", strsource);
|
sprintf!(=> &mut desc, ": '%s'", strsource);
|
||||||
}
|
}
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
ParseTokenType::redirection => {
|
ParseTokenType::redirection => {
|
||||||
let mut desc = WString::from_str("redirection");
|
let mut desc = WString::from_str("redirection");
|
||||||
if let Some(strsource) = n.try_source(orig) {
|
if let Some(strsource) = n.try_source(orig) {
|
||||||
sprintf!(=> &mut desc, ": '%ls'", strsource);
|
sprintf!(=> &mut desc, ": '%s'", strsource);
|
||||||
}
|
}
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
@@ -1864,7 +1804,7 @@ fn visit_mut<N: NodeMut>(&mut self, node: &mut N) -> VisitResult {
|
|||||||
fn will_visit_fields_of<N: NodeMut>(&mut self, node: &mut N) {
|
fn will_visit_fields_of<N: NodeMut>(&mut self, node: &mut N) {
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
ast_construction,
|
ast_construction,
|
||||||
"%*swill_visit %ls",
|
"%*swill_visit %s",
|
||||||
self.spaces(),
|
self.spaces(),
|
||||||
"",
|
"",
|
||||||
node.describe()
|
node.describe()
|
||||||
@@ -1941,7 +1881,7 @@ fn did_visit_fields_of<'a, N: NodeMut>(&'a mut self, node: &'a mut N, flow: Visi
|
|||||||
self,
|
self,
|
||||||
header_kw_range,
|
header_kw_range,
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Missing end to balance this %ls",
|
"Missing end to balance this %s",
|
||||||
enclosing_stmt
|
enclosing_stmt
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1949,7 +1889,7 @@ fn did_visit_fields_of<'a, N: NodeMut>(&'a mut self, node: &'a mut N, flow: Visi
|
|||||||
self,
|
self,
|
||||||
token,
|
token,
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected %ls, but found %ls",
|
"Expected %s, but found %s",
|
||||||
keywords_user_presentable_description(error.allowed_keywords),
|
keywords_user_presentable_description(error.allowed_keywords),
|
||||||
error.token.user_presentable_description(),
|
error.token.user_presentable_description(),
|
||||||
);
|
);
|
||||||
@@ -1967,14 +1907,14 @@ fn visit_optional_mut<N: NodeMut + CheckParse>(&mut self, node: &mut Option<N>)
|
|||||||
fn keywords_user_presentable_description(kws: &'static [ParseKeyword]) -> WString {
|
fn keywords_user_presentable_description(kws: &'static [ParseKeyword]) -> WString {
|
||||||
assert!(!kws.is_empty(), "Should not be empty list");
|
assert!(!kws.is_empty(), "Should not be empty list");
|
||||||
if kws.len() == 1 {
|
if kws.len() == 1 {
|
||||||
return sprintf!("keyword '%ls'", kws[0]);
|
return sprintf!("keyword '%s'", kws[0]);
|
||||||
}
|
}
|
||||||
let mut res = L!("keywords ").to_owned();
|
let mut res = L!("keywords ").to_owned();
|
||||||
for (i, kw) in kws.iter().enumerate() {
|
for (i, kw) in kws.iter().enumerate() {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
res += L!(" or ");
|
res += L!(" or ");
|
||||||
}
|
}
|
||||||
res += &sprintf!("'%ls'", *kw)[..];
|
res += &sprintf!("'%s'", *kw)[..];
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -2092,7 +2032,7 @@ fn list_kind_chomps_newlines(&self, kind: Kind) -> bool {
|
|||||||
internal_error!(
|
internal_error!(
|
||||||
self,
|
self,
|
||||||
list_kind_chomps_newlines,
|
list_kind_chomps_newlines,
|
||||||
"Type %ls not handled",
|
"Type %s not handled",
|
||||||
ast_kind_to_string(kind)
|
ast_kind_to_string(kind)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2142,7 +2082,7 @@ fn list_kind_chomps_semis(&self, kind: Kind) -> bool {
|
|||||||
internal_error!(
|
internal_error!(
|
||||||
self,
|
self,
|
||||||
list_kind_chomps_semis,
|
list_kind_chomps_semis,
|
||||||
"Type %ls not handled",
|
"Type %s not handled",
|
||||||
ast_kind_to_string(kind)
|
ast_kind_to_string(kind)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2215,7 +2155,7 @@ fn consume_token_type(&mut self, typ: ParseTokenType) -> SourceRange {
|
|||||||
self,
|
self,
|
||||||
tok,
|
tok,
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected %ls, but found %ls",
|
"Expected %s, but found %s",
|
||||||
token_type_user_presentable_description(typ, ParseKeyword::None),
|
token_type_user_presentable_description(typ, ParseKeyword::None),
|
||||||
tok.user_presentable_description()
|
tok.user_presentable_description()
|
||||||
);
|
);
|
||||||
@@ -2240,7 +2180,7 @@ fn consume_excess_token_generating_error(&mut self) {
|
|||||||
self,
|
self,
|
||||||
tok,
|
tok,
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected %ls, but found %ls",
|
"Expected %s, but found %s",
|
||||||
token_type_user_presentable_description(ParseTokenType::string, ParseKeyword::None),
|
token_type_user_presentable_description(ParseTokenType::string, ParseKeyword::None),
|
||||||
tok.user_presentable_description()
|
tok.user_presentable_description()
|
||||||
);
|
);
|
||||||
@@ -2279,7 +2219,7 @@ fn consume_excess_token_generating_error(&mut self) {
|
|||||||
internal_error!(
|
internal_error!(
|
||||||
self,
|
self,
|
||||||
consume_excess_token_generating_error,
|
consume_excess_token_generating_error,
|
||||||
"Token %ls should not have prevented parsing a job list",
|
"Token %s should not have prevented parsing a job list",
|
||||||
tok.user_presentable_description()
|
tok.user_presentable_description()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2304,7 +2244,7 @@ fn consume_excess_token_generating_error(&mut self) {
|
|||||||
self,
|
self,
|
||||||
tok,
|
tok,
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected a string, but found %ls",
|
"Expected a string, but found %s",
|
||||||
tok.user_presentable_description()
|
tok.user_presentable_description()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2313,7 +2253,7 @@ fn consume_excess_token_generating_error(&mut self) {
|
|||||||
self,
|
self,
|
||||||
tok,
|
tok,
|
||||||
ParseErrorCode::from(tok.tok_error),
|
ParseErrorCode::from(tok.tok_error),
|
||||||
"%ls",
|
"%s",
|
||||||
tok.tok_error
|
tok.tok_error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2335,7 +2275,7 @@ fn consume_excess_token_generating_error(&mut self) {
|
|||||||
internal_error!(
|
internal_error!(
|
||||||
self,
|
self,
|
||||||
consume_excess_token_generating_error,
|
consume_excess_token_generating_error,
|
||||||
"Unexpected excess token type: %ls",
|
"Unexpected excess token type: %s",
|
||||||
tok.user_presentable_description()
|
tok.user_presentable_description()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2361,7 +2301,7 @@ fn populate_list<Contents, List>(&mut self, list: &mut List, exhaust_stream: boo
|
|||||||
// Mark in the list that it was unwound.
|
// Mark in the list that it was unwound.
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
ast_construction,
|
ast_construction,
|
||||||
"%*sunwinding %ls",
|
"%*sunwinding %s",
|
||||||
self.spaces(),
|
self.spaces(),
|
||||||
"",
|
"",
|
||||||
ast_kind_to_string(list.kind())
|
ast_kind_to_string(list.kind())
|
||||||
@@ -2439,7 +2379,7 @@ fn populate_list<Contents, List>(&mut self, list: &mut List, exhaust_stream: boo
|
|||||||
|
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
ast_construction,
|
ast_construction,
|
||||||
"%*s%ls size: %lu",
|
"%*s%s size: %u",
|
||||||
self.spaces(),
|
self.spaces(),
|
||||||
"",
|
"",
|
||||||
ast_kind_to_string(list.kind()),
|
ast_kind_to_string(list.kind()),
|
||||||
@@ -2463,7 +2403,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
|||||||
slf,
|
slf,
|
||||||
slf.peek_token(0),
|
slf.peek_token(0),
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected %s, but found %ls",
|
"Expected %s, but found %s",
|
||||||
token_type_user_presentable_description(
|
token_type_user_presentable_description(
|
||||||
ParseTokenType::end,
|
ParseTokenType::end,
|
||||||
ParseKeyword::None
|
ParseKeyword::None
|
||||||
@@ -2488,7 +2428,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
|||||||
self,
|
self,
|
||||||
self.peek_token(0),
|
self.peek_token(0),
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected a command, but found %ls",
|
"Expected a command, but found %s",
|
||||||
self.peek_token(0).user_presentable_description()
|
self.peek_token(0).user_presentable_description()
|
||||||
);
|
);
|
||||||
return got_error(self);
|
return got_error(self);
|
||||||
@@ -2577,7 +2517,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> Statement {
|
|||||||
self,
|
self,
|
||||||
self.peek_token(0),
|
self.peek_token(0),
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected a command, but found %ls",
|
"Expected a command, but found %s",
|
||||||
self.peek_token(0).user_presentable_description()
|
self.peek_token(0).user_presentable_description()
|
||||||
);
|
);
|
||||||
return got_error(self);
|
return got_error(self);
|
||||||
@@ -2719,7 +2659,7 @@ fn visit_token(&mut self, token: &mut dyn Token) {
|
|||||||
self,
|
self,
|
||||||
self.peek_token(0),
|
self.peek_token(0),
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected %ls, but found %ls",
|
"Expected %s, but found %s",
|
||||||
token_types_user_presentable_description(token.allowed_tokens()),
|
token_types_user_presentable_description(token.allowed_tokens()),
|
||||||
self.peek_token(0).user_presentable_description()
|
self.peek_token(0).user_presentable_description()
|
||||||
);
|
);
|
||||||
@@ -2763,7 +2703,7 @@ fn visit_keyword(&mut self, keyword: &mut dyn Keyword) -> VisitResult {
|
|||||||
self,
|
self,
|
||||||
self.peek_token(0),
|
self.peek_token(0),
|
||||||
ParseErrorCode::generic,
|
ParseErrorCode::generic,
|
||||||
"Expected %ls, but found %ls",
|
"Expected %s, but found %s",
|
||||||
keywords_user_presentable_description(allowed_keywords),
|
keywords_user_presentable_description(allowed_keywords),
|
||||||
self.peek_token(0).user_presentable_description(),
|
self.peek_token(0).user_presentable_description(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -116,12 +116,12 @@ pub fn resolve_command(&mut self, cmd: &wstr, env: &dyn Environment) -> Option<A
|
|||||||
match &path {
|
match &path {
|
||||||
#[cfg(feature = "embed-data")]
|
#[cfg(feature = "embed-data")]
|
||||||
AutoloadPath::Embedded(_) => {
|
AutoloadPath::Embedded(_) => {
|
||||||
FLOGF!(autoload, "Embedded: %ls", cmd);
|
FLOGF!(autoload, "Embedded: %s", cmd);
|
||||||
}
|
}
|
||||||
AutoloadPath::Path(path) => {
|
AutoloadPath::Path(path) => {
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
autoload,
|
autoload,
|
||||||
"Loading %ls from var %ls from path %ls",
|
"Loading %s from var %s from path %s",
|
||||||
cmd,
|
cmd,
|
||||||
self.env_var_name,
|
self.env_var_name,
|
||||||
path
|
path
|
||||||
@@ -152,14 +152,14 @@ pub fn perform_autoload(path: &AutoloadPath, parser: &Parser) {
|
|||||||
AutoloadPath::Embedded(name) => {
|
AutoloadPath::Embedded(name) => {
|
||||||
use crate::common::str2wcstring;
|
use crate::common::str2wcstring;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
FLOGF!(autoload, "Loading embedded: %ls", name);
|
FLOGF!(autoload, "Loading embedded: %s", name);
|
||||||
let emfile = Asset::get(name).expect("Embedded file not found");
|
let emfile = Asset::get(name).expect("Embedded file not found");
|
||||||
let src = str2wcstring(&emfile.data);
|
let src = str2wcstring(&emfile.data);
|
||||||
let mut widename = L!("embedded:").to_owned();
|
let mut widename = L!("embedded:").to_owned();
|
||||||
widename.push_str(name);
|
widename.push_str(name);
|
||||||
let ret = parser.eval_file_wstr(src, Arc::new(widename), &IoChain::new(), None);
|
let ret = parser.eval_file_wstr(src, Arc::new(widename), &IoChain::new(), None);
|
||||||
if let Err(msg) = ret {
|
if let Err(msg) = ret {
|
||||||
eprintf!("%ls", msg);
|
eprintf!("%s", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -527,8 +527,8 @@ fn touch_file(path: &wstr) {
|
|||||||
.is_none());
|
.is_none());
|
||||||
assert!(autoload.get_autoloaded_commands().is_empty());
|
assert!(autoload.get_autoloaded_commands().is_empty());
|
||||||
|
|
||||||
run!("touch %ls/file1.fish", p1);
|
run!("touch %s/file1.fish", p1);
|
||||||
run!("touch %ls/file2.fish", p2);
|
run!("touch %s/file2.fish", p2);
|
||||||
autoload.invalidate_cache();
|
autoload.invalidate_cache();
|
||||||
|
|
||||||
assert!(!autoload.autoload_in_progress(L!("file1")));
|
assert!(!autoload.autoload_in_progress(L!("file1")));
|
||||||
@@ -586,11 +586,11 @@ fn touch_file(path: &wstr) {
|
|||||||
autoload.resolve_command_impl(L!("file1"), paths),
|
autoload.resolve_command_impl(L!("file1"), paths),
|
||||||
AutoloadResult::Loaded
|
AutoloadResult::Loaded
|
||||||
));
|
));
|
||||||
touch_file(&sprintf!("%ls/file1.fish", p1));
|
touch_file(&sprintf!("%s/file1.fish", p1));
|
||||||
autoload.invalidate_cache();
|
autoload.invalidate_cache();
|
||||||
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_some());
|
assert!(autoload.resolve_command_impl(L!("file1"), paths).is_some());
|
||||||
autoload.mark_autoload_finished(L!("file1"));
|
autoload.mark_autoload_finished(L!("file1"));
|
||||||
|
|
||||||
run!(L!("rm -Rf %ls"), p1);
|
run!(L!("rm -Rf %s"), p1);
|
||||||
run!(L!("rm -Rf %ls"), p2);
|
run!(L!("rm -Rf %s"), p2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ fn source_config_in_directory(parser: &Parser, dir: &wstr) -> bool {
|
|||||||
if waccess(&config_pathname, libc::R_OK) != 0 {
|
if waccess(&config_pathname, libc::R_OK) != 0 {
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
config,
|
config,
|
||||||
"not sourcing %ls (not readable or does not exist)",
|
"not sourcing %s (not readable or does not exist)",
|
||||||
escaped_pathname
|
escaped_pathname
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
@@ -182,7 +182,7 @@ fn read_init(parser: &Parser, paths: &ConfigPaths) {
|
|||||||
let ret = parser.eval_file_wstr(src, fname, &IoChain::new(), None);
|
let ret = parser.eval_file_wstr(src, fname, &IoChain::new(), None);
|
||||||
parser.libdata_mut().within_fish_init = false;
|
parser.libdata_mut().within_fish_init = false;
|
||||||
if let Err(msg) = ret {
|
if let Err(msg) = ret {
|
||||||
eprintf!("%ls", msg);
|
eprintf!("%s", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "embed-data"))]
|
#[cfg(not(feature = "embed-data"))]
|
||||||
@@ -196,7 +196,7 @@ fn read_init(parser: &Parser, paths: &ConfigPaths) {
|
|||||||
let escaped_pathname = escape(&datapath);
|
let escaped_pathname = escape(&datapath);
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
error,
|
error,
|
||||||
"Fish cannot find its asset files in '%ls'.\n\
|
"Fish cannot find its asset files in '%s'.\n\
|
||||||
Refusing to read configuration because of this.",
|
Refusing to read configuration because of this.",
|
||||||
escaped_pathname,
|
escaped_pathname,
|
||||||
);
|
);
|
||||||
@@ -287,7 +287,7 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
|||||||
activate_flog_categories_by_pattern(w.woptarg.unwrap());
|
activate_flog_categories_by_pattern(w.woptarg.unwrap());
|
||||||
for cat in flog::categories::all_categories() {
|
for cat in flog::categories::all_categories() {
|
||||||
if cat.enabled.load(Ordering::Relaxed) {
|
if cat.enabled.load(Ordering::Relaxed) {
|
||||||
printf!("Debug enabled for category: %ls\n", cat.name);
|
printf!("Debug enabled for category: %s\n", cat.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
|||||||
for cat in cats.iter() {
|
for cat in cats.iter() {
|
||||||
let desc = cat.description.localize();
|
let desc = cat.description.localize();
|
||||||
// this is left-justified
|
// this is left-justified
|
||||||
printf!("%-*ls %ls\n", name_width, cat.name, desc);
|
printf!("%-*s %s\n", name_width, cat.name, desc);
|
||||||
}
|
}
|
||||||
return ControlFlow::Break(0);
|
return ControlFlow::Break(0);
|
||||||
}
|
}
|
||||||
@@ -342,21 +342,21 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
|||||||
}
|
}
|
||||||
'?' => {
|
'?' => {
|
||||||
eprintf!(
|
eprintf!(
|
||||||
"%ls\n",
|
"%s\n",
|
||||||
wgettext_fmt!(BUILTIN_ERR_UNKNOWN, "fish", args[w.wopt_index - 1])
|
wgettext_fmt!(BUILTIN_ERR_UNKNOWN, "fish", args[w.wopt_index - 1])
|
||||||
);
|
);
|
||||||
return ControlFlow::Break(1);
|
return ControlFlow::Break(1);
|
||||||
}
|
}
|
||||||
':' => {
|
':' => {
|
||||||
eprintf!(
|
eprintf!(
|
||||||
"%ls\n",
|
"%s\n",
|
||||||
wgettext_fmt!(BUILTIN_ERR_MISSING, "fish", args[w.wopt_index - 1])
|
wgettext_fmt!(BUILTIN_ERR_MISSING, "fish", args[w.wopt_index - 1])
|
||||||
);
|
);
|
||||||
return ControlFlow::Break(1);
|
return ControlFlow::Break(1);
|
||||||
}
|
}
|
||||||
';' => {
|
';' => {
|
||||||
eprintf!(
|
eprintf!(
|
||||||
"%ls\n",
|
"%s\n",
|
||||||
wgettext_fmt!(BUILTIN_ERR_UNEXP_ARG, "fish", args[w.wopt_index - 1])
|
wgettext_fmt!(BUILTIN_ERR_UNEXP_ARG, "fish", args[w.wopt_index - 1])
|
||||||
);
|
);
|
||||||
return ControlFlow::Break(1);
|
return ControlFlow::Break(1);
|
||||||
@@ -623,7 +623,7 @@ fn throwing_main() -> i32 {
|
|||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
warning,
|
warning,
|
||||||
wgettext!("Error while reading file %ls\n"),
|
wgettext!("Error while reading file %s\n"),
|
||||||
path.to_string_lossy()
|
path.to_string_lossy()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -637,10 +637,7 @@ fn throwing_main() -> i32 {
|
|||||||
parser.get_last_status()
|
parser.get_last_status()
|
||||||
};
|
};
|
||||||
|
|
||||||
event::fire(
|
event::fire(parser, Event::process_exit(Pid::new(getpid()), exit_status));
|
||||||
parser,
|
|
||||||
Event::process_exit(Pid::new(getpid()).unwrap(), exit_status),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Trigger any exit handlers.
|
// Trigger any exit handlers.
|
||||||
event::fire_generic(
|
event::fire_generic(
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ fn validate(&mut self, streams: &mut IoStreams) -> bool {
|
|||||||
|
|
||||||
if cmds.len() > 1 {
|
if cmds.len() > 1 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Cannot combine options %ls\n",
|
"%s: Cannot combine options %s\n",
|
||||||
CMD,
|
CMD,
|
||||||
join(&cmds, L!(", "))
|
join(&cmds, L!(", "))
|
||||||
));
|
));
|
||||||
@@ -63,28 +63,26 @@ fn validate(&mut self, streams: &mut IoStreams) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !self.add && self.position.is_some() {
|
if !self.add && self.position.is_some() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams
|
||||||
"%ls: --position option requires --add\n",
|
.err
|
||||||
CMD
|
.append(wgettext_fmt!("%s: --position option requires --add\n", CMD));
|
||||||
));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !self.add && self.regex_pattern.is_some() {
|
if !self.add && self.regex_pattern.is_some() {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: --regex option requires --add\n", CMD));
|
.append(wgettext_fmt!("%s: --regex option requires --add\n", CMD));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !self.add && self.function.is_some() {
|
if !self.add && self.function.is_some() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams
|
||||||
"%ls: --function option requires --add\n",
|
.err
|
||||||
CMD
|
.append(wgettext_fmt!("%s: --function option requires --add\n", CMD));
|
||||||
));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !self.add && self.set_cursor_marker.is_some() {
|
if !self.add && self.set_cursor_marker.is_some() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: --set-cursor option requires --add\n",
|
"%s: --set-cursor option requires --add\n",
|
||||||
CMD
|
CMD
|
||||||
));
|
));
|
||||||
return false;
|
return false;
|
||||||
@@ -96,7 +94,7 @@ fn validate(&mut self, streams: &mut IoStreams) -> bool {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: --set-cursor argument cannot be empty\n",
|
"%s: --set-cursor argument cannot be empty\n",
|
||||||
CMD
|
CMD
|
||||||
));
|
));
|
||||||
return false;
|
return false;
|
||||||
@@ -182,7 +180,7 @@ fn abbr_list(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
const subcmd: &wstr = L!("--list");
|
const subcmd: &wstr = L!("--list");
|
||||||
if !opts.args.is_empty() {
|
if !opts.args.is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls %ls: Unexpected argument -- '%ls'\n",
|
"%s %s: Unexpected argument -- '%s'\n",
|
||||||
CMD,
|
CMD,
|
||||||
subcmd,
|
subcmd,
|
||||||
&opts.args[0]
|
&opts.args[0]
|
||||||
@@ -206,7 +204,7 @@ fn abbr_rename(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
|
|
||||||
if opts.args.len() != 2 {
|
if opts.args.len() != 2 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls %ls: Requires exactly two arguments\n",
|
"%s %s: Requires exactly two arguments\n",
|
||||||
CMD,
|
CMD,
|
||||||
subcmd
|
subcmd
|
||||||
));
|
));
|
||||||
@@ -215,17 +213,15 @@ fn abbr_rename(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
let old_name = &opts.args[0];
|
let old_name = &opts.args[0];
|
||||||
let new_name = &opts.args[1];
|
let new_name = &opts.args[1];
|
||||||
if old_name.is_empty() || new_name.is_empty() {
|
if old_name.is_empty() || new_name.is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams
|
||||||
"%ls %ls: Name cannot be empty\n",
|
.err
|
||||||
CMD,
|
.append(wgettext_fmt!("%s %s: Name cannot be empty\n", CMD, subcmd));
|
||||||
subcmd
|
|
||||||
));
|
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if contains_whitespace(new_name) {
|
if contains_whitespace(new_name) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls %ls: Abbreviation '%ls' cannot have spaces in the word\n",
|
"%s %s: Abbreviation '%s' cannot have spaces in the word\n",
|
||||||
CMD,
|
CMD,
|
||||||
subcmd,
|
subcmd,
|
||||||
new_name.as_utfstr()
|
new_name.as_utfstr()
|
||||||
@@ -235,7 +231,7 @@ fn abbr_rename(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
abbrs::with_abbrs_mut(|abbrs| -> BuiltinResult {
|
abbrs::with_abbrs_mut(|abbrs| -> BuiltinResult {
|
||||||
if !abbrs.has_name(old_name) {
|
if !abbrs.has_name(old_name) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls %ls: No abbreviation named %ls\n",
|
"%s %s: No abbreviation named %s\n",
|
||||||
CMD,
|
CMD,
|
||||||
subcmd,
|
subcmd,
|
||||||
old_name.as_utfstr()
|
old_name.as_utfstr()
|
||||||
@@ -244,7 +240,7 @@ fn abbr_rename(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
}
|
}
|
||||||
if abbrs.has_name(new_name) {
|
if abbrs.has_name(new_name) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls %ls: Abbreviation %ls already exists, cannot rename %ls\n",
|
"%s %s: Abbreviation %s already exists, cannot rename %s\n",
|
||||||
CMD,
|
CMD,
|
||||||
subcmd,
|
subcmd,
|
||||||
new_name.as_utfstr(),
|
new_name.as_utfstr(),
|
||||||
@@ -280,7 +276,7 @@ fn abbr_add(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
|
|
||||||
if opts.args.len() < 2 && opts.function.is_none() {
|
if opts.args.len() < 2 && opts.function.is_none() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls %ls: Requires at least two arguments\n",
|
"%s %s: Requires at least two arguments\n",
|
||||||
CMD,
|
CMD,
|
||||||
subcmd
|
subcmd
|
||||||
));
|
));
|
||||||
@@ -288,17 +284,15 @@ fn abbr_add(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.args.is_empty() || opts.args[0].is_empty() {
|
if opts.args.is_empty() || opts.args[0].is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams
|
||||||
"%ls %ls: Name cannot be empty\n",
|
.err
|
||||||
CMD,
|
.append(wgettext_fmt!("%s %s: Name cannot be empty\n", CMD, subcmd));
|
||||||
subcmd
|
|
||||||
));
|
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
let name = &opts.args[0];
|
let name = &opts.args[0];
|
||||||
if name.chars().any(|c| c.is_whitespace()) {
|
if name.chars().any(|c| c.is_whitespace()) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls %ls: Abbreviation '%ls' cannot have spaces in the word\n",
|
"%s %s: Abbreviation '%s' cannot have spaces in the word\n",
|
||||||
CMD,
|
CMD,
|
||||||
subcmd,
|
subcmd,
|
||||||
name.as_utfstr()
|
name.as_utfstr()
|
||||||
@@ -319,17 +313,15 @@ fn abbr_add(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
|
|
||||||
if let Err(error) = result {
|
if let Err(error) = result {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Regular expression compile error: %ls\n",
|
"%s: Regular expression compile error: %s\n",
|
||||||
CMD,
|
CMD,
|
||||||
error.error_message(),
|
error.error_message(),
|
||||||
));
|
));
|
||||||
if let Some(offset) = error.offset() {
|
if let Some(offset) = error.offset() {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: %ls\n", CMD, regex_pattern.as_utfstr()));
|
.append(wgettext_fmt!("%s: %s\n", CMD, regex_pattern.as_utfstr()));
|
||||||
streams
|
streams.err.append(sprintf!("%s: %*s\n", CMD, offset, "^"));
|
||||||
.err
|
|
||||||
.append(sprintf!("%ls: %*ls\n", CMD, offset, "^"));
|
|
||||||
}
|
}
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
@@ -359,7 +351,7 @@ fn abbr_add(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
// This is to prevent accidental usage of e.g. `--function 'string replace'`
|
// This is to prevent accidental usage of e.g. `--function 'string replace'`
|
||||||
if !valid_func_name(function) || contains_whitespace(function) {
|
if !valid_func_name(function) || contains_whitespace(function) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Invalid function name: %ls\n",
|
"%s: Invalid function name: %s\n",
|
||||||
CMD,
|
CMD,
|
||||||
function.as_utfstr()
|
function.as_utfstr()
|
||||||
));
|
));
|
||||||
@@ -387,7 +379,7 @@ fn abbr_add(opts: &Options, streams: &mut IoStreams) -> BuiltinResult {
|
|||||||
});
|
});
|
||||||
if !opts.commands.is_empty() && position == Position::Command {
|
if !opts.commands.is_empty() && position == Position::Command {
|
||||||
streams.err.appendln(wgettext_fmt!(
|
streams.err.appendln(wgettext_fmt!(
|
||||||
"%ls: --command cannot be combined with --position command",
|
"%s: --command cannot be combined with --position=command",
|
||||||
CMD,
|
CMD,
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -501,7 +493,7 @@ pub fn abbr(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
'p' => {
|
'p' => {
|
||||||
if opts.position.is_some() {
|
if opts.position.is_some() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Cannot specify multiple positions\n",
|
"%s: Cannot specify multiple positions\n",
|
||||||
CMD
|
CMD
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -512,7 +504,7 @@ pub fn abbr(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
opts.position = Some(Position::Anywhere);
|
opts.position = Some(Position::Anywhere);
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Invalid position '%ls'\n",
|
"%s: Invalid position '%s'\n",
|
||||||
CMD,
|
CMD,
|
||||||
w.woptarg.unwrap_or_default()
|
w.woptarg.unwrap_or_default()
|
||||||
));
|
));
|
||||||
@@ -525,7 +517,7 @@ pub fn abbr(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
'r' => {
|
'r' => {
|
||||||
if opts.regex_pattern.is_some() {
|
if opts.regex_pattern.is_some() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Cannot specify multiple regex patterns\n",
|
"%s: Cannot specify multiple regex patterns\n",
|
||||||
CMD
|
CMD
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -535,7 +527,7 @@ pub fn abbr(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
SET_CURSOR_SHORT => {
|
SET_CURSOR_SHORT => {
|
||||||
if opts.set_cursor_marker.is_some() {
|
if opts.set_cursor_marker.is_some() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Cannot specify multiple set-cursor options\n",
|
"%s: Cannot specify multiple set-cursor options\n",
|
||||||
CMD
|
CMD
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -558,7 +550,7 @@ pub fn abbr(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
'U' => {
|
'U' => {
|
||||||
// Kept and made ineffective, so we warn.
|
// Kept and made ineffective, so we warn.
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Warning: Option '%ls' was removed and is now ignored",
|
"%s: Warning: Option '%s' was removed and is now ignored",
|
||||||
cmd,
|
cmd,
|
||||||
argv_read[w.wopt_index - 1]
|
argv_read[w.wopt_index - 1]
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
localizable_consts!(
|
localizable_consts!(
|
||||||
BUILTIN_ERR_INVALID_OPT_SPEC
|
BUILTIN_ERR_INVALID_OPT_SPEC
|
||||||
"%ls: Invalid option spec '%ls' at char '%lc'\n"
|
"%s: Invalid option spec '%s' at char '%c'\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -146,7 +146,7 @@ fn check_for_mutually_exclusive_flags(
|
|||||||
std::mem::swap(&mut flag1, &mut flag2);
|
std::mem::swap(&mut flag1, &mut flag2);
|
||||||
}
|
}
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls %ls: options cannot be used together\n",
|
"%s: %s %s: options cannot be used together\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
flag1,
|
flag1,
|
||||||
flag2
|
flag2
|
||||||
@@ -168,7 +168,7 @@ fn parse_exclusive_args(opts: &mut ArgParseCmdOpts, streams: &mut IoStreams) ->
|
|||||||
let xflags: Vec<_> = raw_xflags.split(',').collect();
|
let xflags: Vec<_> = raw_xflags.split(',').collect();
|
||||||
if xflags.len() < 2 {
|
if xflags.len() < 2 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: exclusive flag string '%ls' is not valid\n",
|
"%s: exclusive flag string '%s' is not valid\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
raw_xflags
|
raw_xflags
|
||||||
));
|
));
|
||||||
@@ -186,7 +186,7 @@ fn parse_exclusive_args(opts: &mut ArgParseCmdOpts, streams: &mut IoStreams) ->
|
|||||||
exclusive_set.push(*short_equiv);
|
exclusive_set.push(*short_equiv);
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: exclusive flag '%ls' is not valid\n",
|
"%s: exclusive flag '%s' is not valid\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
flag
|
flag
|
||||||
));
|
));
|
||||||
@@ -215,7 +215,7 @@ fn parse_flag_modifiers<'args>(
|
|||||||
&& s.char_at(0) != '&'
|
&& s.char_at(0) != '&'
|
||||||
{
|
{
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Implicit int short flag '%lc' does not allow modifiers like '%lc'\n",
|
"%s: Implicit int short flag '%c' does not allow modifiers like '%c'\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
opt_spec.short_flag,
|
opt_spec.short_flag,
|
||||||
s.char_at(0)
|
s.char_at(0)
|
||||||
@@ -277,7 +277,7 @@ fn parse_flag_modifiers<'args>(
|
|||||||
|
|
||||||
if opts.options.contains_key(&opt_spec.short_flag) {
|
if opts.options.contains_key(&opt_spec.short_flag) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Short flag '%lc' already defined\n",
|
"%s: Short flag '%c' already defined\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
opt_spec.short_flag
|
opt_spec.short_flag
|
||||||
));
|
));
|
||||||
@@ -309,7 +309,7 @@ fn parse_option_spec_sep<'args>(
|
|||||||
}
|
}
|
||||||
if opts.implicit_int_flag != '\0' {
|
if opts.implicit_int_flag != '\0' {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Implicit int flag '%lc' already defined\n",
|
"%s: Implicit int flag '%c' already defined\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
opts.implicit_int_flag
|
opts.implicit_int_flag
|
||||||
));
|
));
|
||||||
@@ -351,7 +351,7 @@ fn parse_option_spec_sep<'args>(
|
|||||||
'#' => {
|
'#' => {
|
||||||
if opts.implicit_int_flag != '\0' {
|
if opts.implicit_int_flag != '\0' {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Implicit int flag '%lc' already defined\n",
|
"%s: Implicit int flag '%c' already defined\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
opts.implicit_int_flag
|
opts.implicit_int_flag
|
||||||
));
|
));
|
||||||
@@ -395,7 +395,7 @@ fn parse_option_spec<'args>(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
if option_spec.is_empty() {
|
if option_spec.is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: An option spec must have at least a short or a long flag\n",
|
"%s: An option spec must have at least a short or a long flag\n",
|
||||||
opts.name
|
opts.name
|
||||||
));
|
));
|
||||||
return false;
|
return false;
|
||||||
@@ -405,7 +405,7 @@ fn parse_option_spec<'args>(
|
|||||||
if !fish_iswalnum(s.char_at(0)) && s.char_at(0) != '#' && !(s.char_at(0) == '/' && s.len() > 1)
|
if !fish_iswalnum(s.char_at(0)) && s.char_at(0) != '#' && !(s.char_at(0) == '/' && s.len() > 1)
|
||||||
{
|
{
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Short flag '%lc' invalid, must be alphanum or '#'\n",
|
"%s: Short flag '%c' invalid, must be alphanum or '#'\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
s.char_at(0)
|
s.char_at(0)
|
||||||
));
|
));
|
||||||
@@ -432,7 +432,7 @@ fn parse_option_spec<'args>(
|
|||||||
opt_spec.long_flag = s.slice_to(long_flag_char_count);
|
opt_spec.long_flag = s.slice_to(long_flag_char_count);
|
||||||
if opts.long_to_short_flag.contains_key(opt_spec.long_flag) {
|
if opts.long_to_short_flag.contains_key(opt_spec.long_flag) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Long flag '%ls' already defined\n",
|
"%s: Long flag '%s' already defined\n",
|
||||||
opts.name,
|
opts.name,
|
||||||
opt_spec.long_flag
|
opt_spec.long_flag
|
||||||
));
|
));
|
||||||
@@ -480,7 +480,7 @@ fn collect_option_specs<'args>(
|
|||||||
if *optind == argc {
|
if *optind == argc {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Missing -- separator\n", cmd));
|
.append(wgettext_fmt!("%s: Missing -- separator\n", cmd));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +502,7 @@ fn collect_option_specs<'args>(
|
|||||||
if counter > counter_max {
|
if counter > counter_max {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Too many long-only options\n", cmd));
|
.append(wgettext_fmt!("%s: Too many long-only options\n", cmd));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ fn parse_cmd_opts<'args>(
|
|||||||
ArgType::NoArgument
|
ArgType::NoArgument
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Invalid --unknown-arguments value '%ls'\n",
|
"%s: Invalid --unknown-arguments value '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
kind
|
kind
|
||||||
));
|
));
|
||||||
@@ -574,7 +574,7 @@ fn parse_cmd_opts<'args>(
|
|||||||
let x = fish_wcstol(w.woptarg.unwrap()).unwrap_or(-1);
|
let x = fish_wcstol(w.woptarg.unwrap()).unwrap_or(-1);
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Invalid --min-args value '%ls'\n",
|
"%s: Invalid --min-args value '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
w.woptarg.unwrap()
|
w.woptarg.unwrap()
|
||||||
));
|
));
|
||||||
@@ -588,7 +588,7 @@ fn parse_cmd_opts<'args>(
|
|||||||
let x = fish_wcstol(w.woptarg.unwrap()).unwrap_or(-1);
|
let x = fish_wcstol(w.woptarg.unwrap()).unwrap_or(-1);
|
||||||
if x < 0 {
|
if x < 0 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Invalid --max-args value '%ls'\n",
|
"%s: Invalid --max-args value '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
w.woptarg.unwrap()
|
w.woptarg.unwrap()
|
||||||
));
|
));
|
||||||
@@ -642,7 +642,7 @@ fn parse_cmd_opts<'args>(
|
|||||||
// The user didn't specify any option specs.
|
// The user didn't specify any option specs.
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Missing -- separator\n", cmd));
|
.append(wgettext_fmt!("%s: Missing -- separator\n", cmd));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Implementation of the bg builtin.
|
// Implementation of the bg builtin.
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::proc::Pid;
|
use crate::proc::Pid;
|
||||||
|
|
||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
@@ -16,7 +18,7 @@ fn send_to_bg(
|
|||||||
if !jobs[job_pos].wants_job_control() {
|
if !jobs[job_pos].wants_job_control() {
|
||||||
let job = &jobs[job_pos];
|
let job = &jobs[job_pos];
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Can't put job %s, '%ls' to background because it is not under job control\n",
|
"%s: Can't put job %s, '%s' to background because it is not under job control\n",
|
||||||
cmd,
|
cmd,
|
||||||
job.job_id().to_wstring(),
|
job.job_id().to_wstring(),
|
||||||
job.command()
|
job.command()
|
||||||
@@ -26,7 +28,7 @@ fn send_to_bg(
|
|||||||
|
|
||||||
let job = &jobs[job_pos];
|
let job = &jobs[job_pos];
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"Send job %s '%ls' to background\n",
|
"Send job %s '%s' to background\n",
|
||||||
job.job_id().to_wstring(),
|
job.job_id().to_wstring(),
|
||||||
job.command()
|
job.command()
|
||||||
));
|
));
|
||||||
@@ -66,7 +68,7 @@ pub fn bg(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Built
|
|||||||
let Some(job_pos) = job_pos else {
|
let Some(job_pos) = job_pos else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: There are no suitable jobs\n", cmd));
|
.append(wgettext_fmt!("%s: There are no suitable jobs\n", cmd));
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,14 +81,9 @@ pub fn bg(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Built
|
|||||||
let mut retval: BuiltinResult = Ok(SUCCESS);
|
let mut retval: BuiltinResult = Ok(SUCCESS);
|
||||||
let pids: Vec<Pid> = args[opts.optind..]
|
let pids: Vec<Pid> = args[opts.optind..]
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|arg| match fish_wcstoi(arg).map(Pid::new) {
|
.filter_map(|arg| match parse_pid(streams, cmd, arg) {
|
||||||
Ok(Some(pid)) => Some(pid),
|
Ok(pid) => Some(pid),
|
||||||
_ => {
|
_ => {
|
||||||
streams.err.append(wgettext_fmt!(
|
|
||||||
"%ls: '%ls' is not a valid job specifier\n",
|
|
||||||
cmd,
|
|
||||||
arg
|
|
||||||
));
|
|
||||||
retval = Err(STATUS_INVALID_ARGS);
|
retval = Err(STATUS_INVALID_ARGS);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -97,13 +94,16 @@ pub fn bg(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Built
|
|||||||
|
|
||||||
// Background all existing jobs that match the pids.
|
// Background all existing jobs that match the pids.
|
||||||
// Non-existent jobs aren't an error, but information about them is useful.
|
// Non-existent jobs aren't an error, but information about them is useful.
|
||||||
|
let mut seen = HashSet::new();
|
||||||
for pid in pids {
|
for pid in pids {
|
||||||
if let Some((job_pos, _job)) = parser.job_get_with_index_from_pid(pid) {
|
if let Some((job_pos, job)) = parser.job_get_with_index_from_pid(pid) {
|
||||||
send_to_bg(parser, streams, cmd, job_pos)?;
|
if seen.insert(&*job as *const _) {
|
||||||
|
send_to_bg(parser, streams, cmd, job_pos)?;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Could not find job '%d'\n", cmd, pid));
|
.append(wgettext_fmt!("%s: Could not find job '%d'\n", cmd, pid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -350,13 +350,13 @@ fn insert(
|
|||||||
if !self.opts.silent {
|
if !self.opts.silent {
|
||||||
if seq.len() == 1 {
|
if seq.len() == 1 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: No binding found for key '%ls'\n",
|
"%s: No binding found for key '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
seq[0]
|
seq[0]
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: No binding found for key sequence '%ls'\n",
|
"%s: No binding found for key sequence '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
eseq
|
eseq
|
||||||
));
|
));
|
||||||
@@ -432,7 +432,7 @@ fn parse_cmd_opts(
|
|||||||
'h' => opts.print_help = true,
|
'h' => opts.print_help = true,
|
||||||
'k' => {
|
'k' => {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: the -k/--key syntax is no longer supported. See `bind --help` and `bind --key-names`\n",
|
"%s: the -k/--key syntax is no longer supported. See `bind --help` and `bind --key-names`\n",
|
||||||
cmd,
|
cmd,
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -555,7 +555,7 @@ pub fn bind(
|
|||||||
_ => {
|
_ => {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Invalid state\n", cmd));
|
.append(wgettext_fmt!("%s: Invalid state\n", cmd));
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ pub fn block(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Bu
|
|||||||
if opts.erase {
|
if opts.erase {
|
||||||
if opts.scope != Scope::Unset {
|
if opts.scope != Scope::Unset {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Can not specify scope when removing block\n",
|
"%s: Can not specify scope when removing block\n",
|
||||||
cmd
|
cmd
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -99,7 +99,7 @@ pub fn block(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Bu
|
|||||||
if parser.global_event_blocks.load(Ordering::Relaxed) == 0 {
|
if parser.global_event_blocks.load(Ordering::Relaxed) == 0 {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: No blocks defined\n", cmd));
|
.append(wgettext_fmt!("%s: No blocks defined\n", cmd));
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
}
|
}
|
||||||
parser.global_event_blocks.fetch_sub(1, Ordering::Relaxed);
|
parser.global_event_blocks.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
|||||||
5
src/builtins/break.rs
Normal file
5
src/builtins/break.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use super::prelude::*;
|
||||||
|
|
||||||
|
pub fn r#break(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> BuiltinResult {
|
||||||
|
builtin_break_continue(parser, streams, argv)
|
||||||
|
}
|
||||||
44
src/builtins/breakpoint.rs
Normal file
44
src/builtins/breakpoint.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use super::prelude::*;
|
||||||
|
use crate::parser::{Block, BlockType};
|
||||||
|
use crate::reader::reader_read;
|
||||||
|
use libc::STDIN_FILENO;
|
||||||
|
|
||||||
|
/// Implementation of the builtin breakpoint command, used to launch the interactive debugger.
|
||||||
|
pub fn breakpoint(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> BuiltinResult {
|
||||||
|
let cmd = argv[0];
|
||||||
|
if argv.len() != 1 {
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
BUILTIN_ERR_ARG_COUNT1,
|
||||||
|
cmd,
|
||||||
|
0,
|
||||||
|
argv.len() - 1
|
||||||
|
));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're not interactive then we can't enter the debugger. So treat this command as a no-op.
|
||||||
|
if !parser.is_interactive() {
|
||||||
|
return Err(STATUS_CMD_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we don't allow creating a breakpoint at an interactive prompt. There may be a simpler
|
||||||
|
// or clearer way to do this but this works.
|
||||||
|
{
|
||||||
|
if parser
|
||||||
|
.block_at_index(1)
|
||||||
|
.map_or(true, |b| b.typ() == BlockType::breakpoint)
|
||||||
|
{
|
||||||
|
streams.err.append(wgettext_fmt!(
|
||||||
|
"%s: Command not valid at an interactive prompt\n",
|
||||||
|
cmd,
|
||||||
|
));
|
||||||
|
return Err(STATUS_ILLEGAL_CMD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let bpb = parser.push_block(Block::breakpoint_block());
|
||||||
|
let io_chain = &streams.io_chain;
|
||||||
|
reader_read(parser, STDIN_FILENO, io_chain)?;
|
||||||
|
parser.pop_block(bpb);
|
||||||
|
BuiltinResult::from_dynamic(parser.get_last_status())
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Built
|
|||||||
None => {
|
None => {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Could not find home directory\n", cmd));
|
.append(wgettext_fmt!("%s: Could not find home directory\n", cmd));
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Built
|
|||||||
// Stop `cd ""` from crashing
|
// Stop `cd ""` from crashing
|
||||||
if dir_in.is_empty() {
|
if dir_in.is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Empty directory '%ls' does not exist\n",
|
"%s: Empty directory '%s' does not exist\n",
|
||||||
cmd,
|
cmd,
|
||||||
dir_in
|
dir_in
|
||||||
));
|
));
|
||||||
@@ -63,7 +63,7 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Built
|
|||||||
let dirs = path_apply_cdpath(dir_in, &pwd, vars);
|
let dirs = path_apply_cdpath(dir_in, &pwd, vars);
|
||||||
if dirs.is_empty() {
|
if dirs.is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: The directory '%ls' does not exist\n",
|
"%s: The directory '%s' does not exist\n",
|
||||||
cmd,
|
cmd,
|
||||||
dir_in
|
dir_in
|
||||||
));
|
));
|
||||||
@@ -132,41 +132,37 @@ pub fn cd(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Built
|
|||||||
}
|
}
|
||||||
|
|
||||||
if best_errno == ENOTDIR {
|
if best_errno == ENOTDIR {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams
|
||||||
"%ls: '%ls' is not a directory\n",
|
.err
|
||||||
cmd,
|
.append(wgettext_fmt!("%s: '%s' is not a directory\n", cmd, dir_in));
|
||||||
dir_in
|
|
||||||
));
|
|
||||||
} else if !broken_symlink.is_empty() {
|
} else if !broken_symlink.is_empty() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: '%ls' is a broken symbolic link to '%ls'\n",
|
"%s: '%s' is a broken symbolic link to '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
broken_symlink,
|
broken_symlink,
|
||||||
broken_symlink_target
|
broken_symlink_target
|
||||||
));
|
));
|
||||||
} else if best_errno == ELOOP {
|
} else if best_errno == ELOOP {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Too many levels of symbolic links: '%ls'\n",
|
"%s: Too many levels of symbolic links: '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
dir_in
|
dir_in
|
||||||
));
|
));
|
||||||
} else if best_errno == ENOENT {
|
} else if best_errno == ENOENT {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: The directory '%ls' does not exist\n",
|
"%s: The directory '%s' does not exist\n",
|
||||||
cmd,
|
cmd,
|
||||||
dir_in
|
dir_in
|
||||||
));
|
));
|
||||||
} else if best_errno == EACCES || best_errno == EPERM {
|
} else if best_errno == EACCES || best_errno == EPERM {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams
|
||||||
"%ls: Permission denied: '%ls'\n",
|
.err
|
||||||
cmd,
|
.append(wgettext_fmt!("%s: Permission denied: '%s'\n", cmd, dir_in));
|
||||||
dir_in
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
errno::set_errno(Errno(best_errno));
|
errno::set_errno(Errno(best_errno));
|
||||||
wperror(L!("cd"));
|
wperror(L!("cd"));
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Unknown error trying to locate directory '%ls'\n",
|
"%s: Unknown error trying to locate directory '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
dir_in
|
dir_in
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
|||||||
let Some(cmd) = input_function_get_code(arg) else {
|
let Some(cmd) = input_function_get_code(arg) else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Unknown input function '%ls'", cmd, arg));
|
.append(wgettext_fmt!("%s: Unknown input function '%s'", cmd, arg));
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
};
|
};
|
||||||
@@ -517,7 +517,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
|||||||
let Ok(new_coord) = usize::try_from(new_coord) else {
|
let Ok(new_coord) = usize::try_from(new_coord) else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: line/column index starts at 1", cmd));
|
.append(wgettext_fmt!("%s: line/column index starts at 1", cmd));
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
};
|
};
|
||||||
@@ -529,7 +529,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
|||||||
) else {
|
) else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: there is no line %ls\n", cmd, arg));
|
.append(wgettext_fmt!("%s: there is no line %s\n", cmd, arg));
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
};
|
};
|
||||||
@@ -544,7 +544,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
|||||||
.unwrap_or(rstate.text.len());
|
.unwrap_or(rstate.text.len());
|
||||||
if line_offset + new_coord > next_line_offset {
|
if line_offset + new_coord > next_line_offset {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: column %ls exceeds line length\n",
|
"%s: column %s exceeds line length\n",
|
||||||
cmd,
|
cmd,
|
||||||
arg
|
arg
|
||||||
));
|
));
|
||||||
@@ -601,7 +601,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
|||||||
let Some(selection) = rstate.selection else {
|
let Some(selection) = rstate.selection else {
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
};
|
};
|
||||||
streams.out.append(sprintf!("%lu\n", selection.start));
|
streams.out.append(sprintf!("%u\n", selection.start));
|
||||||
return Ok(SUCCESS);
|
return Ok(SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,7 +609,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
|||||||
let Some(selection) = rstate.selection else {
|
let Some(selection) = rstate.selection else {
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
};
|
};
|
||||||
streams.out.append(sprintf!("%lu\n", selection.end));
|
streams.out.append(sprintf!("%u\n", selection.end));
|
||||||
return Ok(SUCCESS);
|
return Ok(SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -716,7 +716,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
|||||||
} else {
|
} else {
|
||||||
streams
|
streams
|
||||||
.out
|
.out
|
||||||
.append(sprintf!("%lu\n", current_cursor_pos - range.start));
|
.append(sprintf!("%u\n", current_cursor_pos - range.start));
|
||||||
}
|
}
|
||||||
return Ok(SUCCESS);
|
return Ok(SUCCESS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Invalid token '%ls'\n",
|
"%s: Invalid token '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
w.woptarg.unwrap()
|
w.woptarg.unwrap()
|
||||||
));
|
));
|
||||||
@@ -318,7 +318,7 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
|||||||
if arg.is_empty() {
|
if arg.is_empty() {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: -s requires a non-empty string\n", cmd,));
|
.append(wgettext_fmt!("%s: -s requires a non-empty string\n", cmd,));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,7 +328,7 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
|||||||
if arg.is_empty() {
|
if arg.is_empty() {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: -l requires a non-empty string\n", cmd,));
|
.append(wgettext_fmt!("%s: -l requires a non-empty string\n", cmd,));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
|||||||
if arg.is_empty() {
|
if arg.is_empty() {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: -o requires a non-empty string\n", cmd,));
|
.append(wgettext_fmt!("%s: -o requires a non-empty string\n", cmd,));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,7 +442,7 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
|||||||
|
|
||||||
if let Err(err_text) = parse_util_detect_errors_in_argument_list(&comp, &prefix) {
|
if let Err(err_text) = parse_util_detect_errors_in_argument_list(&comp, &prefix) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls: contains a syntax error\n",
|
"%s: %s: contains a syntax error\n",
|
||||||
cmd,
|
cmd,
|
||||||
comp
|
comp
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ pub fn contains(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
|
|||||||
} else {
|
} else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Key not specified\n", cmd));
|
.append(wgettext_fmt!("%s: Key not specified\n", cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
|
|||||||
5
src/builtins/continue.rs
Normal file
5
src/builtins/continue.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use super::prelude::*;
|
||||||
|
|
||||||
|
pub fn r#continue(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> BuiltinResult {
|
||||||
|
builtin_break_continue(parser, streams, argv)
|
||||||
|
}
|
||||||
@@ -3,12 +3,8 @@
|
|||||||
use super::prelude::*;
|
use super::prelude::*;
|
||||||
use crate::io::IoStreams;
|
use crate::io::IoStreams;
|
||||||
use crate::parser::Parser;
|
use crate::parser::Parser;
|
||||||
use crate::proc::{add_disowned_job, Job, Pid};
|
use crate::proc::{add_disowned_job, Job};
|
||||||
use crate::{
|
use crate::{builtins::shared::HelpOnlyCmdOpts, wchar::wstr, wutil::wgettext_fmt};
|
||||||
builtins::shared::HelpOnlyCmdOpts,
|
|
||||||
wchar::wstr,
|
|
||||||
wutil::{fish_wcstoi, wgettext_fmt},
|
|
||||||
};
|
|
||||||
use libc::SIGCONT;
|
use libc::SIGCONT;
|
||||||
|
|
||||||
/// Helper for builtin_disown.
|
/// Helper for builtin_disown.
|
||||||
@@ -27,7 +23,7 @@ fn disown_job(cmd: &wstr, streams: &mut IoStreams, j: &Job) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: job %d ('%ls') was stopped and has been signalled to continue.\n",
|
"%s: job %d ('%s') was stopped and has been signalled to continue.\n",
|
||||||
cmd,
|
cmd,
|
||||||
j.job_id(),
|
j.job_id(),
|
||||||
j.command()
|
j.command()
|
||||||
@@ -71,7 +67,7 @@ pub fn disown(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B
|
|||||||
} else {
|
} else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: There are no suitable jobs\n", cmd));
|
.append(wgettext_fmt!("%s: There are no suitable jobs\n", cmd));
|
||||||
retval = Err(STATUS_CMD_ERROR);
|
retval = Err(STATUS_CMD_ERROR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -80,31 +76,23 @@ pub fn disown(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B
|
|||||||
// If one argument is not a valid pid (i.e. integer >= 0), fail without disowning anything,
|
// If one argument is not a valid pid (i.e. integer >= 0), fail without disowning anything,
|
||||||
// but still print errors for all of them.
|
// but still print errors for all of them.
|
||||||
// Non-existent jobs aren't an error, but information about them is useful.
|
// Non-existent jobs aren't an error, but information about them is useful.
|
||||||
let mut jobs: Vec<_> = args[1..]
|
let mut jobs: Vec<_> = args[opts.optind..]
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|arg| {
|
.filter_map(|arg| {
|
||||||
// Attempt to convert the argument to a PID.
|
let pid = match parse_pid(streams, cmd, arg) {
|
||||||
match fish_wcstoi(arg).ok().and_then(Pid::new) {
|
Ok(pid) => pid,
|
||||||
None => {
|
Err(code) => {
|
||||||
// Invalid identifier
|
retval = Err(code);
|
||||||
streams.err.append(wgettext_fmt!(
|
return None;
|
||||||
"%ls: '%ls' is not a valid job specifier\n",
|
|
||||||
cmd,
|
|
||||||
arg
|
|
||||||
));
|
|
||||||
retval = Err(STATUS_INVALID_ARGS);
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
Some(pid) => parser.job_get_from_pid(pid).or_else(|| {
|
};
|
||||||
// Valid identifier but no such job
|
parser.job_get_from_pid(pid).or_else(|| {
|
||||||
streams.err.append(wgettext_fmt!(
|
// Valid identifier but no such job
|
||||||
"%ls: Could not find job '%d'\n",
|
streams
|
||||||
cmd,
|
.err
|
||||||
pid
|
.append(wgettext_fmt!("%s: Could not find job '%d'\n", cmd, pid));
|
||||||
));
|
None
|
||||||
None
|
})
|
||||||
}),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub fn emit(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
let Some(event_name) = argv.get(opts.optind) else {
|
let Some(event_name) = argv.get(opts.optind) else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(sprintf!(L!("%ls: expected event name\n"), cmd));
|
.append(sprintf!(L!("%s: expected event name\n"), cmd));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
5
src/builtins/false.rs
Normal file
5
src/builtins/false.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use super::prelude::*;
|
||||||
|
|
||||||
|
pub fn r#false(_parser: &Parser, _streams: &mut IoStreams, _argv: &mut [&wstr]) -> BuiltinResult {
|
||||||
|
Err(STATUS_CMD_ERROR)
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
//! Implementation of the fg builtin.
|
//! Implementation of the fg builtin.
|
||||||
|
|
||||||
use crate::fds::make_fd_blocking;
|
use crate::fds::make_fd_blocking;
|
||||||
use crate::proc::Pid;
|
|
||||||
use crate::reader::{reader_save_screen_state, reader_write_title};
|
use crate::reader::{reader_save_screen_state, reader_write_title};
|
||||||
use crate::tokenizer::tok_command;
|
use crate::tokenizer::tok_command;
|
||||||
use crate::wutil::perror;
|
use crate::wutil::perror;
|
||||||
@@ -38,7 +37,7 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Built
|
|||||||
None => {
|
None => {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: There are no suitable jobs\n", cmd));
|
.append(wgettext_fmt!("%s: There are no suitable jobs\n", cmd));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
Some((pos, j)) => {
|
Some((pos, j)) => {
|
||||||
@@ -49,47 +48,35 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Built
|
|||||||
} else if optind + 1 < argv.len() {
|
} else if optind + 1 < argv.len() {
|
||||||
// Specifying more than one job to put to the foreground is a syntax error, we still
|
// Specifying more than one job to put to the foreground is a syntax error, we still
|
||||||
// try to locate the job $argv[1], since we need to determine which error message to
|
// try to locate the job $argv[1], since we need to determine which error message to
|
||||||
// emit (ambiguous job specification vs malformed job id).
|
// emit (ambiguous job specification vs malformed job ID).
|
||||||
let mut found_job = false;
|
let mut found_job = false;
|
||||||
if let Ok(Some(pid)) = fish_wcstoi(argv[optind]).map(Pid::new) {
|
if let Ok(pid) = parse_pid(streams, cmd, argv[optind]) {
|
||||||
found_job = parser.job_get_from_pid(pid).is_some();
|
found_job = parser.job_get_from_pid(pid).is_some();
|
||||||
};
|
};
|
||||||
|
|
||||||
if found_job {
|
if found_job {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: Ambiguous job\n", cmd));
|
.append(wgettext_fmt!("%s: Ambiguous job\n", cmd));
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams
|
||||||
"%ls: '%ls' is not a job\n",
|
.err
|
||||||
cmd,
|
.append(wgettext_fmt!("%s: '%s' is not a job\n", cmd, argv[optind]));
|
||||||
argv[optind]
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
job_pos = None;
|
job_pos = None;
|
||||||
job = None;
|
job = None;
|
||||||
} else {
|
} else {
|
||||||
match fish_wcstoi(argv[optind]) {
|
match parse_pid(streams, cmd, argv[optind]) {
|
||||||
Err(_) => {
|
|
||||||
streams
|
|
||||||
.err
|
|
||||||
.append(wgettext_fmt!(BUILTIN_ERR_NOT_NUMBER, cmd, argv[optind]));
|
|
||||||
job_pos = None;
|
|
||||||
job = None;
|
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
|
||||||
}
|
|
||||||
Ok(pid) => {
|
Ok(pid) => {
|
||||||
let raw_pid = pid;
|
let j = parser.job_get_with_index_from_pid(pid);
|
||||||
let pid = Pid::new(pid.abs());
|
|
||||||
let j = pid.and_then(|pid| parser.job_get_with_index_from_pid(pid));
|
|
||||||
if j.as_ref()
|
if j.as_ref()
|
||||||
.map_or(true, |(_pos, j)| !j.is_constructed() || j.is_completed())
|
.map_or(true, |(_pos, j)| !j.is_constructed() || j.is_completed())
|
||||||
{
|
{
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: No suitable job: %d\n", cmd, raw_pid));
|
.append(wgettext_fmt!("%s: No suitable job: %d\n", cmd, pid));
|
||||||
job_pos = None;
|
job_pos = None;
|
||||||
job = None
|
job = None
|
||||||
} else {
|
} else {
|
||||||
@@ -97,18 +84,23 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Built
|
|||||||
job_pos = Some(pos);
|
job_pos = Some(pos);
|
||||||
job = if !j.wants_job_control() {
|
job = if !j.wants_job_control() {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Can't put job %d, '%ls' to foreground because it is not under job control\n",
|
"%s: Can't put job %d, '%s' to foreground because it is not under job control\n",
|
||||||
cmd,
|
cmd,
|
||||||
raw_pid,
|
pid,
|
||||||
j.command()
|
j.command()
|
||||||
));
|
));
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(j)
|
Some(j)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
Err(_err) => {
|
||||||
|
job_pos = None;
|
||||||
|
job = None;
|
||||||
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(job) = job else {
|
let Some(job) = job else {
|
||||||
|
|||||||
@@ -997,7 +997,7 @@ enum OutputType {
|
|||||||
if args.is_empty() && i == 0 {
|
if args.is_empty() && i == 0 {
|
||||||
if output_type == OutputType::File {
|
if output_type == OutputType::File {
|
||||||
streams.err.appendln(wgettext_fmt!(
|
streams.err.appendln(wgettext_fmt!(
|
||||||
"Expected file path to read/write for -w:\n\n $ %ls -w foo.fish",
|
"Expected file path to read/write for -w:\n\n $ %s -w foo.fish",
|
||||||
PROGRAM_NAME.get().unwrap()
|
PROGRAM_NAME.get().unwrap()
|
||||||
));
|
));
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
@@ -1299,7 +1299,7 @@ fn html_colorize(text: &wstr, colors: &[HighlightSpec]) -> Vec<u8> {
|
|||||||
html.push_str("</span>");
|
html.push_str("</span>");
|
||||||
}
|
}
|
||||||
if i == 0 || color != last_color {
|
if i == 0 || color != last_color {
|
||||||
sprintf!(=> &mut html, "<span class=\"%ls\">", html_class_name_for_color(color));
|
sprintf!(=> &mut html, "<span class=\"%s\">", html_class_name_for_color(color));
|
||||||
}
|
}
|
||||||
last_color = color;
|
last_color = color;
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ fn parse_flags(
|
|||||||
}
|
}
|
||||||
'v' => {
|
'v' => {
|
||||||
streams.out.appendln(wgettext_fmt!(
|
streams.out.appendln(wgettext_fmt!(
|
||||||
"%ls, version %s",
|
"%s, version %s",
|
||||||
PROGRAM_NAME.get().unwrap(),
|
PROGRAM_NAME.get().unwrap(),
|
||||||
crate::BUILD_VERSION
|
crate::BUILD_VERSION
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ fn parse_cmd_opts(
|
|||||||
argv: &mut [&wstr],
|
argv: &mut [&wstr],
|
||||||
parser: &Parser,
|
parser: &Parser,
|
||||||
streams: &mut IoStreams,
|
streams: &mut IoStreams,
|
||||||
) -> c_int {
|
) -> BuiltinResult {
|
||||||
let cmd = L!("function");
|
let cmd = L!("function");
|
||||||
let print_hints = false;
|
let print_hints = false;
|
||||||
let mut handling_named_arguments = false;
|
let mut handling_named_arguments = false;
|
||||||
@@ -94,20 +94,20 @@ fn parse_cmd_opts(
|
|||||||
if handling_named_arguments {
|
if handling_named_arguments {
|
||||||
if is_read_only(&woptarg) {
|
if is_read_only(&woptarg) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: variable '%ls' is read-only\n",
|
"%s: variable '%s' is read-only\n",
|
||||||
cmd,
|
cmd,
|
||||||
woptarg
|
woptarg
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
opts.named_arguments.push(woptarg);
|
opts.named_arguments.push(woptarg);
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls: unexpected positional argument",
|
"%s: %s: unexpected positional argument",
|
||||||
cmd,
|
cmd,
|
||||||
woptarg
|
woptarg
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'd' => {
|
'd' => {
|
||||||
@@ -116,11 +116,11 @@ fn parse_cmd_opts(
|
|||||||
's' => {
|
's' => {
|
||||||
let Some(signal) = Signal::parse(w.woptarg.unwrap()) else {
|
let Some(signal) = Signal::parse(w.woptarg.unwrap()) else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Unknown signal '%ls'",
|
"%s: Unknown signal '%s'",
|
||||||
cmd,
|
cmd,
|
||||||
w.woptarg.unwrap()
|
w.woptarg.unwrap()
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
};
|
};
|
||||||
opts.events.push(EventDescription::Signal { signal });
|
opts.events.push(EventDescription::Signal { signal });
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ fn parse_cmd_opts(
|
|||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, name));
|
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, name));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
opts.events.push(EventDescription::Variable { name });
|
opts.events.push(EventDescription::Variable { name });
|
||||||
}
|
}
|
||||||
@@ -149,34 +149,26 @@ fn parse_cmd_opts(
|
|||||||
};
|
};
|
||||||
if caller_id == 0 {
|
if caller_id == 0 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: calling job for event handler not found",
|
"%s: calling job for event handler not found",
|
||||||
cmd
|
cmd
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
e = EventDescription::CallerExit { caller_id };
|
e = EventDescription::CallerExit { caller_id };
|
||||||
} else if opt == 'p' && woptarg == "%self" {
|
} else if opt == 'p' && woptarg == "%self" {
|
||||||
let pid = Pid::new(getpid());
|
let pid = Pid::new(getpid());
|
||||||
e = EventDescription::ProcessExit { pid };
|
e = EventDescription::ProcessExit { pid: Some(pid) };
|
||||||
} else {
|
} else {
|
||||||
let Ok(pid @ 0..) = fish_wcstoi(woptarg) else {
|
let pid = parse_pid_may_be_zero(streams, cmd, woptarg)?;
|
||||||
streams.err.append(wgettext_fmt!(
|
|
||||||
"%ls: %ls: invalid process id",
|
|
||||||
cmd,
|
|
||||||
woptarg
|
|
||||||
));
|
|
||||||
return STATUS_INVALID_ARGS;
|
|
||||||
};
|
|
||||||
if opt == 'p' {
|
if opt == 'p' {
|
||||||
e = EventDescription::ProcessExit { pid: Pid::new(pid) };
|
e = EventDescription::ProcessExit { pid };
|
||||||
} else {
|
} else {
|
||||||
// TODO: rationalize why a default of 0 is sensible.
|
// TODO: rationalize why a default of 0 is sensible.
|
||||||
let internal_job_id = match Pid::new(pid) {
|
let internal_job_id = pid
|
||||||
Some(pid) => job_id_for_pid(pid, parser).unwrap_or(0),
|
.and_then(|pid| job_id_for_pid(pid, parser))
|
||||||
None => 0,
|
.unwrap_or_default();
|
||||||
};
|
|
||||||
e = EventDescription::JobExit {
|
e = EventDescription::JobExit {
|
||||||
pid: Pid::new(pid),
|
pid,
|
||||||
internal_job_id,
|
internal_job_id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -187,11 +179,11 @@ fn parse_cmd_opts(
|
|||||||
let name = w.woptarg.unwrap().to_owned();
|
let name = w.woptarg.unwrap().to_owned();
|
||||||
if is_read_only(&name) {
|
if is_read_only(&name) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: variable '%ls' is read-only\n",
|
"%s: variable '%s' is read-only\n",
|
||||||
cmd,
|
cmd,
|
||||||
name
|
name
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
handling_named_arguments = true;
|
handling_named_arguments = true;
|
||||||
opts.named_arguments.push(name);
|
opts.named_arguments.push(name);
|
||||||
@@ -208,7 +200,7 @@ fn parse_cmd_opts(
|
|||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, woptarg));
|
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, woptarg));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
opts.inherit_vars.push(woptarg.to_owned());
|
opts.inherit_vars.push(woptarg.to_owned());
|
||||||
}
|
}
|
||||||
@@ -217,7 +209,7 @@ fn parse_cmd_opts(
|
|||||||
}
|
}
|
||||||
':' => {
|
':' => {
|
||||||
builtin_missing_argument(parser, streams, cmd, argv[w.wopt_index - 1], print_hints);
|
builtin_missing_argument(parser, streams, cmd, argv[w.wopt_index - 1], print_hints);
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
';' => {
|
';' => {
|
||||||
builtin_unexpected_argument(
|
builtin_unexpected_argument(
|
||||||
@@ -227,11 +219,11 @@ fn parse_cmd_opts(
|
|||||||
argv[w.wopt_index - 1],
|
argv[w.wopt_index - 1],
|
||||||
print_hints,
|
print_hints,
|
||||||
);
|
);
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
'?' => {
|
'?' => {
|
||||||
builtin_unknown_option(parser, streams, cmd, argv[w.wopt_index - 1], print_hints);
|
builtin_unknown_option(parser, streams, cmd, argv[w.wopt_index - 1], print_hints);
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
panic!("Unexpected retval from WGetopter: {}", other);
|
panic!("Unexpected retval from WGetopter: {}", other);
|
||||||
@@ -240,27 +232,39 @@ fn parse_cmd_opts(
|
|||||||
}
|
}
|
||||||
|
|
||||||
*optind = w.wopt_index;
|
*optind = w.wopt_index;
|
||||||
STATUS_CMD_OK
|
Ok(SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_function_name(function_name: &wstr, cmd: &wstr, streams: &mut IoStreams) -> c_int {
|
fn validate_function_name(
|
||||||
|
argv: &mut [&wstr],
|
||||||
|
function_name: &mut WString,
|
||||||
|
cmd: &wstr,
|
||||||
|
streams: &mut IoStreams,
|
||||||
|
) -> BuiltinResult {
|
||||||
|
if argv.len() < 2 {
|
||||||
|
streams
|
||||||
|
.err
|
||||||
|
.append(wgettext_fmt!("%ls: function name required", cmd));
|
||||||
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
}
|
||||||
|
*function_name = argv[1].to_owned();
|
||||||
if !valid_func_name(function_name) {
|
if !valid_func_name(function_name) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls: invalid function name",
|
"%s: %s: invalid function name",
|
||||||
cmd,
|
cmd,
|
||||||
function_name,
|
function_name,
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
if parser_keywords_is_reserved(function_name) {
|
if parser_keywords_is_reserved(function_name) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls: cannot use reserved keyword as function name",
|
"%s: %s: cannot use reserved keyword as function name",
|
||||||
cmd,
|
cmd,
|
||||||
function_name
|
function_name
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
STATUS_CMD_OK
|
Ok(SUCCESS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a function. Calls into `function.rs` to perform the heavy lifting of defining a
|
/// Define a function. Calls into `function.rs` to perform the heavy lifting of defining a
|
||||||
@@ -271,7 +275,7 @@ pub fn function(
|
|||||||
streams: &mut IoStreams,
|
streams: &mut IoStreams,
|
||||||
c_args: &mut [&wstr],
|
c_args: &mut [&wstr],
|
||||||
func_node: NodeRef<BlockStatement>,
|
func_node: NodeRef<BlockStatement>,
|
||||||
) -> c_int {
|
) -> BuiltinResult {
|
||||||
// The wgetopt function expects 'function' as the first argument. Make a new vec with
|
// The wgetopt function expects 'function' as the first argument. Make a new vec with
|
||||||
// that property. This is needed because this builtin has a different signature than the other
|
// that property. This is needed because this builtin has a different signature than the other
|
||||||
// builtins.
|
// builtins.
|
||||||
@@ -281,23 +285,17 @@ pub fn function(
|
|||||||
let cmd = argv[0];
|
let cmd = argv[0];
|
||||||
|
|
||||||
// A valid function name has to be the first argument.
|
// A valid function name has to be the first argument.
|
||||||
let function_name = argv[1].to_owned();
|
let mut function_name = WString::new();
|
||||||
let mut retval = validate_function_name(&function_name, cmd, streams);
|
validate_function_name(argv, &mut function_name, cmd, streams)?;
|
||||||
if retval != STATUS_CMD_OK {
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
let argv = &mut argv[1..];
|
let argv = &mut argv[1..];
|
||||||
|
|
||||||
let mut opts = FunctionCmdOpts::default();
|
let mut opts = FunctionCmdOpts::default();
|
||||||
let mut optind = 0;
|
let mut optind = 0;
|
||||||
retval = parse_cmd_opts(&mut opts, &mut optind, argv, parser, streams);
|
parse_cmd_opts(&mut opts, &mut optind, argv, parser, streams)?;
|
||||||
if retval != STATUS_CMD_OK {
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.print_help {
|
if opts.print_help {
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
return STATUS_CMD_OK;
|
return Ok(SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if argv.len() != optind {
|
if argv.len() != optind {
|
||||||
@@ -308,17 +306,17 @@ pub fn function(
|
|||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, arg));
|
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, arg));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
opts.named_arguments.push(arg.to_owned());
|
opts.named_arguments.push(arg.to_owned());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls: unexpected positional argument",
|
"%s: %s: unexpected positional argument",
|
||||||
cmd,
|
cmd,
|
||||||
argv[optind],
|
argv[optind],
|
||||||
));
|
));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,7 +341,7 @@ pub fn function(
|
|||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, named));
|
.append(wgettext_fmt!(BUILTIN_ERR_VARNAME, cmd, named));
|
||||||
return STATUS_INVALID_ARGS;
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,5 +396,5 @@ pub fn function(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
STATUS_CMD_OK
|
Ok(SUCCESS)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
if let Some(desc) = opts.description {
|
if let Some(desc) = opts.description {
|
||||||
if args.len() != 1 {
|
if args.len() != 1 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Expected exactly one function name\n",
|
"%s: Expected exactly one function name\n",
|
||||||
cmd
|
cmd
|
||||||
));
|
));
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
@@ -182,7 +182,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
|
|
||||||
if !function::exists(current_func, parser) {
|
if !function::exists(current_func, parser) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Function '%ls' does not exist\n",
|
"%s: Function '%s' does not exist\n",
|
||||||
cmd,
|
cmd,
|
||||||
current_func
|
current_func
|
||||||
));
|
));
|
||||||
@@ -280,7 +280,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
&& !event::EVENT_FILTER_NAMES.contains(&opts.handlers_type.unwrap())
|
&& !event::EVENT_FILTER_NAMES.contains(&opts.handlers_type.unwrap())
|
||||||
{
|
{
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Expected generic | variable | signal | exit | job-id for --handlers-type\n",
|
"%s: Expected generic | variable | signal | exit | job-id for --handlers-type\n",
|
||||||
cmd
|
cmd
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
@@ -320,7 +320,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
if opts.copy {
|
if opts.copy {
|
||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Expected exactly two names (current function name, and new function name)\n",
|
"%s: Expected exactly two names (current function name, and new function name)\n",
|
||||||
cmd
|
cmd
|
||||||
));
|
));
|
||||||
builtin_print_error_trailer(parser, streams.err, cmd);
|
builtin_print_error_trailer(parser, streams.err, cmd);
|
||||||
@@ -331,7 +331,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
|
|
||||||
if !function::exists(current_func, parser) {
|
if !function::exists(current_func, parser) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Function '%ls' does not exist\n",
|
"%s: Function '%s' does not exist\n",
|
||||||
cmd,
|
cmd,
|
||||||
current_func
|
current_func
|
||||||
));
|
));
|
||||||
@@ -341,7 +341,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
|
|
||||||
if !valid_func_name(new_func) || parser_keywords_is_reserved(new_func) {
|
if !valid_func_name(new_func) || parser_keywords_is_reserved(new_func) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Illegal function name '%ls'\n",
|
"%s: Illegal function name '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
new_func
|
new_func
|
||||||
));
|
));
|
||||||
@@ -351,7 +351,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
|
|
||||||
if function::exists(new_func, parser) {
|
if function::exists(new_func, parser) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: Function '%ls' already exists. Cannot create copy of '%ls'\n",
|
"%s: Function '%s' already exists. Cannot create copy of '%s'\n",
|
||||||
cmd,
|
cmd,
|
||||||
new_func,
|
new_func,
|
||||||
current_func
|
current_func
|
||||||
@@ -391,7 +391,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
}
|
}
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
comment.push_utfstr(&wgettext_fmt!(
|
comment.push_utfstr(&wgettext_fmt!(
|
||||||
"Defined in %ls @ line %d",
|
"Defined in %s @ line %d",
|
||||||
path,
|
path,
|
||||||
props.definition_lineno()
|
props.definition_lineno()
|
||||||
));
|
));
|
||||||
@@ -406,7 +406,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
}
|
}
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
comment.push_utfstr(&wgettext_fmt!(
|
comment.push_utfstr(&wgettext_fmt!(
|
||||||
", copied in %ls @ line %d",
|
", copied in %s @ line %d",
|
||||||
path,
|
path,
|
||||||
props.copy_definition_lineno()
|
props.copy_definition_lineno()
|
||||||
));
|
));
|
||||||
@@ -420,7 +420,7 @@ pub fn functions(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -
|
|||||||
|
|
||||||
if !comment.is_empty() {
|
if !comment.is_empty() {
|
||||||
def.push_utfstr(&sprintf!(
|
def.push_utfstr(&sprintf!(
|
||||||
"# %ls\n%ls",
|
"# %s\n%s",
|
||||||
comment,
|
comment,
|
||||||
props.annotated_definition(arg)
|
props.annotated_definition(arg)
|
||||||
));
|
));
|
||||||
|
|||||||
14
src/builtins/gettext.rs
Normal file
14
src/builtins/gettext.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use super::prelude::*;
|
||||||
|
|
||||||
|
/// Used for the fish `_` builtin for requesting translations.
|
||||||
|
/// For scripts in `share/`, the corresponding strings are extracted from the scripts using
|
||||||
|
/// `build_tools/fish_xgettext.fish`.
|
||||||
|
/// Strings not present in our repo would require a custom MO file for translation to be possible.
|
||||||
|
pub fn gettext(_parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> BuiltinResult {
|
||||||
|
for arg in &argv[1..] {
|
||||||
|
streams.out.append(
|
||||||
|
crate::wutil::LocalizableString::from_external_source((*arg).to_owned()).localize(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(SUCCESS)
|
||||||
|
}
|
||||||
@@ -113,7 +113,7 @@ fn check_for_unexpected_hist_args(
|
|||||||
if opts.search_type.is_some() || opts.show_time_format.is_some() || opts.null_terminate {
|
if opts.search_type.is_some() || opts.show_time_format.is_some() || opts.null_terminate {
|
||||||
let subcmd_str = opts.hist_cmd.to_wstr();
|
let subcmd_str = opts.hist_cmd.to_wstr();
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls: subcommand takes no options\n",
|
"%s: %s: subcommand takes no options\n",
|
||||||
cmd,
|
cmd,
|
||||||
subcmd_str
|
subcmd_str
|
||||||
));
|
));
|
||||||
@@ -335,7 +335,7 @@ pub fn history(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
|
|||||||
|
|
||||||
if in_private_mode(parser.vars()) {
|
if in_private_mode(parser.vars()) {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: can't merge history in private mode\n",
|
"%s: can't merge history in private mode\n",
|
||||||
cmd
|
cmd
|
||||||
));
|
));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
use crate::io::IoStreams;
|
use crate::io::IoStreams;
|
||||||
use crate::job_group::{JobId, MaybeJobId};
|
use crate::job_group::{JobId, MaybeJobId};
|
||||||
use crate::parser::Parser;
|
use crate::parser::Parser;
|
||||||
use crate::proc::{clock_ticks_to_seconds, have_proc_stat, proc_get_jiffies, Job, Pid};
|
use crate::proc::{clock_ticks_to_seconds, have_proc_stat, proc_get_jiffies, Job};
|
||||||
use crate::wchar_ext::WExt;
|
use crate::wchar_ext::WExt;
|
||||||
use crate::wgetopt::{wopt, ArgType, WGetopter, WOption};
|
use crate::wgetopt::{wopt, ArgType, WGetopter, WOption};
|
||||||
use crate::wutil::wgettext;
|
use crate::wutil::wgettext;
|
||||||
@@ -22,7 +22,7 @@ enum JobsPrintMode {
|
|||||||
Default, // print lots of general info
|
Default, // print lots of general info
|
||||||
PrintPid, // print pid of each process in job
|
PrintPid, // print pid of each process in job
|
||||||
PrintCommand, // print command name of each process in job
|
PrintCommand, // print command name of each process in job
|
||||||
PrintGroup, // print group id of job
|
PrintGroup, // print group ID of job
|
||||||
PrintNothing, // print nothing (exit status only)
|
PrintNothing, // print nothing (exit status only)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ fn builtin_jobs_print(j: &Job, mode: JobsPrintMode, header: bool, streams: &mut
|
|||||||
}
|
}
|
||||||
|
|
||||||
for p in j.processes() {
|
for p in j.processes() {
|
||||||
out += &sprintf!("%ls\n", p.argv0().unwrap())[..];
|
out += &sprintf!("%s\n", p.argv0().unwrap())[..];
|
||||||
}
|
}
|
||||||
streams.out.append(out);
|
streams.out.append(out);
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ pub fn jobs(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
match fish_wcstoi(&arg[1..]).ok().filter(|&job_id| job_id >= 0) {
|
match fish_wcstoi(&arg[1..]).ok().filter(|&job_id| job_id >= 0) {
|
||||||
None => {
|
None => {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: '%ls' is not a valid job id\n",
|
"%s: '%s' is not a valid job ID\n",
|
||||||
cmd,
|
cmd,
|
||||||
arg
|
arg
|
||||||
));
|
));
|
||||||
@@ -214,19 +214,8 @@ pub fn jobs(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match fish_wcstoi(arg).ok().and_then(Pid::new) {
|
let pid = parse_pid(streams, cmd, arg)?;
|
||||||
None => {
|
j = parser.job_get_from_pid(pid)
|
||||||
streams.err.append(wgettext_fmt!(
|
|
||||||
"%ls: '%ls' is not a valid process id\n",
|
|
||||||
cmd,
|
|
||||||
arg
|
|
||||||
));
|
|
||||||
return Err(STATUS_INVALID_ARGS);
|
|
||||||
}
|
|
||||||
Some(job_id) => {
|
|
||||||
j = parser.job_get_from_pid(job_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(j) = j.filter(|j| !j.is_completed() && j.is_constructed()) {
|
if let Some(j) = j.filter(|j| !j.is_completed() && j.is_constructed()) {
|
||||||
@@ -236,7 +225,7 @@ pub fn jobs(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
if mode != JobsPrintMode::PrintNothing {
|
if mode != JobsPrintMode::PrintNothing {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: No suitable job: %ls\n", cmd, arg));
|
.append(wgettext_fmt!("%s: No suitable job: %s\n", cmd, arg));
|
||||||
}
|
}
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
}
|
}
|
||||||
@@ -256,7 +245,7 @@ pub fn jobs(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Bui
|
|||||||
if !streams.out_is_redirected && mode != JobsPrintMode::PrintNothing {
|
if !streams.out_is_redirected && mode != JobsPrintMode::PrintNothing {
|
||||||
streams
|
streams
|
||||||
.out
|
.out
|
||||||
.append(wgettext_fmt!("%ls: There are no jobs\n", argv[0]));
|
.append(wgettext_fmt!("%s: There are no jobs\n", argv[0]));
|
||||||
}
|
}
|
||||||
return Err(STATUS_CMD_ERROR);
|
return Err(STATUS_CMD_ERROR);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ fn parse_cmd_opts(
|
|||||||
if scale < 0 || scale > 15 {
|
if scale < 0 || scale > 15 {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: %ls: invalid scale\n", cmd, optarg));
|
.append(wgettext_fmt!("%s: %s: invalid scale\n", cmd, optarg));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
// We know the value is in the range [0, 15]
|
// We know the value is in the range [0, 15]
|
||||||
@@ -89,7 +89,7 @@ fn parse_cmd_opts(
|
|||||||
} else {
|
} else {
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(wgettext_fmt!("%ls: %ls: invalid mode\n", cmd, optarg));
|
.append(wgettext_fmt!("%s: %s: invalid mode\n", cmd, optarg));
|
||||||
return Err(STATUS_INVALID_ARGS);
|
return Err(STATUS_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ fn parse_cmd_opts(
|
|||||||
let base = fish_wcstoi(optarg).unwrap_or(-1);
|
let base = fish_wcstoi(optarg).unwrap_or(-1);
|
||||||
if base != 8 && base != 16 {
|
if base != 8 && base != 16 {
|
||||||
streams.err.append(wgettext_fmt!(
|
streams.err.append(wgettext_fmt!(
|
||||||
"%ls: %ls: invalid base value\n",
|
"%s: %s: invalid base value\n",
|
||||||
cmd,
|
cmd,
|
||||||
optarg
|
optarg
|
||||||
));
|
));
|
||||||
@@ -159,7 +159,7 @@ fn format_double(mut v: f64, opts: &Options) -> WString {
|
|||||||
if opts.base == 16 {
|
if opts.base == 16 {
|
||||||
v = v.trunc();
|
v = v.trunc();
|
||||||
let mneg = if v.is_sign_negative() { "-" } else { "" };
|
let mneg = if v.is_sign_negative() { "-" } else { "" };
|
||||||
return sprintf!("%s0x%lx", mneg, v.abs() as u64);
|
return sprintf!("%s0x%x", mneg, v.abs() as u64);
|
||||||
} else if opts.base == 8 {
|
} else if opts.base == 8 {
|
||||||
v = v.trunc();
|
v = v.trunc();
|
||||||
if v == 0.0 {
|
if v == 0.0 {
|
||||||
@@ -167,7 +167,7 @@ fn format_double(mut v: f64, opts: &Options) -> WString {
|
|||||||
return WString::from_str("0");
|
return WString::from_str("0");
|
||||||
}
|
}
|
||||||
let mneg = if v.is_sign_negative() { "-" } else { "" };
|
let mneg = if v.is_sign_negative() { "-" } else { "" };
|
||||||
return sprintf!("%s0%lo", mneg, v.abs() as u64);
|
return sprintf!("%s0%o", mneg, v.abs() as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
v *= pow(10f64, opts.scale);
|
v *= pow(10f64, opts.scale);
|
||||||
@@ -245,24 +245,24 @@ fn evaluate_expression(
|
|||||||
|
|
||||||
streams
|
streams
|
||||||
.err
|
.err
|
||||||
.append(sprintf!("%ls: Error: %ls\n", cmd, error_message));
|
.append(sprintf!("%s: Error: %s\n", cmd, error_message));
|
||||||
streams.err.append(sprintf!("'%ls'\n", expression));
|
streams.err.append(sprintf!("'%s'\n", expression));
|
||||||
|
|
||||||
Err(STATUS_CMD_ERROR)
|
Err(STATUS_CMD_ERROR)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
streams.err.append(sprintf!(
|
streams.err.append(sprintf!(
|
||||||
L!("%ls: Error: %ls\n"),
|
L!("%s: Error: %s\n"),
|
||||||
cmd,
|
cmd,
|
||||||
err.kind.describe_wstr()
|
err.kind.describe_wstr()
|
||||||
));
|
));
|
||||||
streams.err.append(sprintf!("'%ls'\n", expression));
|
streams.err.append(sprintf!("'%s'\n", expression));
|
||||||
let padding = WString::from_chars(vec![' '; err.position + 1]);
|
let padding = WString::from_chars(vec![' '; err.position + 1]);
|
||||||
if err.len >= 2 {
|
if err.len >= 2 {
|
||||||
let tildes = WString::from_chars(vec!['~'; err.len - 2]);
|
let tildes = WString::from_chars(vec!['~'; err.len - 2]);
|
||||||
streams.err.append(sprintf!("%ls^%ls^\n", padding, tildes));
|
streams.err.append(sprintf!("%s^%s^\n", padding, tildes));
|
||||||
} else {
|
} else {
|
||||||
streams.err.append(sprintf!("%ls^\n", padding));
|
streams.err.append(sprintf!("%s^\n", padding));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(STATUS_CMD_ERROR)
|
Err(STATUS_CMD_ERROR)
|
||||||
|
|||||||
@@ -5,23 +5,28 @@
|
|||||||
pub mod bg;
|
pub mod bg;
|
||||||
pub mod bind;
|
pub mod bind;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod r#break;
|
||||||
|
pub mod breakpoint;
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
pub mod cd;
|
pub mod cd;
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod commandline;
|
pub mod commandline;
|
||||||
pub mod complete;
|
pub mod complete;
|
||||||
pub mod contains;
|
pub mod contains;
|
||||||
|
pub mod r#continue;
|
||||||
pub mod count;
|
pub mod count;
|
||||||
pub mod disown;
|
pub mod disown;
|
||||||
pub mod echo;
|
pub mod echo;
|
||||||
pub mod emit;
|
pub mod emit;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod exit;
|
pub mod exit;
|
||||||
|
pub mod r#false;
|
||||||
pub mod fg;
|
pub mod fg;
|
||||||
pub mod fish_indent;
|
pub mod fish_indent;
|
||||||
pub mod fish_key_reader;
|
pub mod fish_key_reader;
|
||||||
pub mod function;
|
pub mod function;
|
||||||
pub mod functions;
|
pub mod functions;
|
||||||
|
pub mod r#gettext;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
pub mod jobs;
|
pub mod jobs;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
@@ -38,6 +43,7 @@
|
|||||||
pub mod status;
|
pub mod status;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
pub mod r#true;
|
||||||
pub mod r#type;
|
pub mod r#type;
|
||||||
pub mod ulimit;
|
pub mod ulimit;
|
||||||
pub mod wait;
|
pub mod wait;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user