mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-04-19 14:51:13 -03:00
Compare commits
172 Commits
4.0.8
...
test-drive
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc63404c96 | ||
|
|
5507bcc425 | ||
|
|
c3442602ef | ||
|
|
3437d63507 | ||
|
|
695ae02aa5 | ||
|
|
e1059b5e43 | ||
|
|
f1cf64fba0 | ||
|
|
8304fd0fd0 | ||
|
|
f4f786633d | ||
|
|
ec3b3fe321 | ||
|
|
d3762f11b5 | ||
|
|
6db0f39676 | ||
|
|
7970ca55af | ||
|
|
ccbbae95ef | ||
|
|
6d551b4459 | ||
|
|
cc9083e220 | ||
|
|
14df28382d | ||
|
|
e6d57f2fb2 | ||
|
|
4def0ac616 | ||
|
|
b574a5e4f6 | ||
|
|
9b67b2ae07 | ||
|
|
ea4e4a4279 | ||
|
|
3405621dee | ||
|
|
834001087d | ||
|
|
e697add5b5 | ||
|
|
704b911168 | ||
|
|
af137e5e96 | ||
|
|
75832b3c5d | ||
|
|
dda4371679 | ||
|
|
10f1f21a4f | ||
|
|
109ef88831 | ||
|
|
f9b79926f1 | ||
|
|
6c3150aa05 | ||
|
|
07dd088d76 | ||
|
|
93e0a33d41 | ||
|
|
dcddffd222 | ||
|
|
d842a6560e | ||
|
|
b4e8cc8b79 | ||
|
|
83eb25d45f | ||
|
|
7eb254f2ba | ||
|
|
2e12a2b6c4 | ||
|
|
a780e4da15 | ||
|
|
33dd823f45 | ||
|
|
e11e62674f | ||
|
|
e49dde87cc | ||
|
|
edfdf210c4 | ||
|
|
996fec87f4 | ||
|
|
870a8f77a0 | ||
|
|
d823444c6e | ||
|
|
abaeb4af2a | ||
|
|
670541eec8 | ||
|
|
0debddc9e5 | ||
|
|
837c32f150 | ||
|
|
9b26fff278 | ||
|
|
2b46d97c68 | ||
|
|
65ced4e2bb | ||
|
|
3710142d1d | ||
|
|
0c9c5e3a34 | ||
|
|
53912777af | ||
|
|
70bd49f612 | ||
|
|
6714818e5d | ||
|
|
7e9b35be48 | ||
|
|
7af9844de0 | ||
|
|
3129c9e939 | ||
|
|
967c4b2272 | ||
|
|
1c4e5cadf2 | ||
|
|
532abaddae | ||
|
|
7049352e61 | ||
|
|
a4f4ae76cb | ||
|
|
c3de539d46 | ||
|
|
7bb38355e8 | ||
|
|
5520ee3c65 | ||
|
|
e66f6878b5 | ||
|
|
e9b9ee8d63 | ||
|
|
17d57b70d0 | ||
|
|
cb3d004a5a | ||
|
|
5e10d75a19 | ||
|
|
050fe09af1 | ||
|
|
b531cc8b43 | ||
|
|
63e705a778 | ||
|
|
1df8de06c1 | ||
|
|
943adf4dd0 | ||
|
|
53dc7772eb | ||
|
|
64ed47bf4e | ||
|
|
6848e70e87 | ||
|
|
d5efef1cc5 | ||
|
|
e715c3e3ff | ||
|
|
13763fa318 | ||
|
|
8910390602 | ||
|
|
b6c2a4c5db | ||
|
|
a88de9d345 | ||
|
|
e8801d2ced | ||
|
|
1d620356f8 | ||
|
|
8bb6597b9b | ||
|
|
8ae12973df | ||
|
|
459fc3c887 | ||
|
|
a719f9d537 | ||
|
|
da0a93b24b | ||
|
|
8bb442f135 | ||
|
|
3fcc6482cb | ||
|
|
83b0294fc9 | ||
|
|
84f19a931d | ||
|
|
3201cb9f01 | ||
|
|
e1e963ae66 | ||
|
|
ca9c5f4cec | ||
|
|
48ae19b4b1 | ||
|
|
7ec1487016 | ||
|
|
bc26481558 | ||
|
|
41e82c8c9e | ||
|
|
376bf3a982 | ||
|
|
1e384900fa | ||
|
|
cde503b0a8 | ||
|
|
69f0d960cf | ||
|
|
ca28d0a78f | ||
|
|
6043644f52 | ||
|
|
1227b6765c | ||
|
|
905c7310c6 | ||
|
|
b1064ac3a0 | ||
|
|
1bda6043c8 | ||
|
|
d8d5913159 | ||
|
|
2ac1523e54 | ||
|
|
9cea5e0732 | ||
|
|
6f9ca42a30 | ||
|
|
b8df9648f2 | ||
|
|
66b80041cc | ||
|
|
a579abb81b | ||
|
|
b97598fa6c | ||
|
|
64cb86ac26 | ||
|
|
a14906f52f | ||
|
|
36d7049749 | ||
|
|
4b9767ce83 | ||
|
|
f6d76d2057 | ||
|
|
659c926dbd | ||
|
|
56da15d11f | ||
|
|
5e59762117 | ||
|
|
69fdbc89d6 | ||
|
|
244c55f9ce | ||
|
|
b7ae159824 | ||
|
|
6dad396498 | ||
|
|
f5a02e590d | ||
|
|
36c632889b | ||
|
|
c473aa60a7 | ||
|
|
6515862095 | ||
|
|
94dfe1b053 | ||
|
|
0b52b72ebc | ||
|
|
eade6a5672 | ||
|
|
044cea1bf3 | ||
|
|
74b1247461 | ||
|
|
9b8793a2df | ||
|
|
6c63139d23 | ||
|
|
f3dd4ee022 | ||
|
|
7bafb0d1ae | ||
|
|
46072e0fd6 | ||
|
|
c09a9246a1 | ||
|
|
e2596d13cd | ||
|
|
0153579a4c | ||
|
|
c1b460525c | ||
|
|
5de6f4bb3d | ||
|
|
54cc932215 | ||
|
|
e3864c752a | ||
|
|
03a9f4a775 | ||
|
|
7e5af914be | ||
|
|
ab4606430e | ||
|
|
774b7c7b5b | ||
|
|
6b1a9ef7ce | ||
|
|
c74afd4198 | ||
|
|
3dc49d9d93 | ||
|
|
b19a467ea6 | ||
|
|
381b38af0a | ||
|
|
965bc78d33 | ||
|
|
610338cc70 | ||
|
|
f9fb026085 |
@@ -24,4 +24,4 @@ tasks:
|
||||
ninja
|
||||
- test: |
|
||||
cd fish-shell/build
|
||||
env ninja test
|
||||
ninja test
|
||||
|
||||
@@ -20,4 +20,4 @@ tasks:
|
||||
ninja
|
||||
- test: |
|
||||
cd fish/build
|
||||
env ninja test
|
||||
ninja test
|
||||
|
||||
@@ -20,7 +20,6 @@ linux_task:
|
||||
# container:
|
||||
# <<: *step
|
||||
# image: ghcr.io/krobelus/fish-ci/focal-32bit:latest
|
||||
|
||||
tests_script:
|
||||
# cirrus at times gives us 32 procs and 2 GB of RAM
|
||||
# Unrestriced parallelism results in OOM
|
||||
@@ -30,7 +29,6 @@ linux_task:
|
||||
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCTEST_PARALLEL_LEVEL=6 ..
|
||||
- ninja -j 6 fish
|
||||
- ninja fish_run_tests
|
||||
|
||||
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
linux_arm_task:
|
||||
@@ -41,7 +39,6 @@ linux_arm_task:
|
||||
- name: jammy-armv7-32bit
|
||||
arm_container:
|
||||
image: ghcr.io/fish-shell/fish-ci/jammy-armv7-32bit
|
||||
|
||||
tests_script:
|
||||
# cirrus at times gives us 32 procs and 2 GB of RAM
|
||||
# Unrestriced parallelism results in OOM
|
||||
@@ -52,7 +49,6 @@ linux_arm_task:
|
||||
- ninja -j 6 fish
|
||||
- file ./fish
|
||||
- ninja fish_run_tests
|
||||
|
||||
# CI task disabled during RIIR transition
|
||||
only_if: false && $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
@@ -92,5 +88,4 @@ freebsd_task:
|
||||
- sudo -u fish-user -s cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCTEST_PARALLEL_LEVEL=1 ..
|
||||
- sudo -u fish-user sh -c '. $HOME/.cargo/env; ninja -j 6 fish'
|
||||
- sudo -u fish-user sh -c '. $HOME/.cargo/env; ninja fish_run_tests'
|
||||
|
||||
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
@@ -22,7 +22,7 @@ indent_size = 2
|
||||
indent_size = 2
|
||||
|
||||
[share/{completions,functions}/**.fish]
|
||||
max_line_length = off
|
||||
max_line_length = unset
|
||||
|
||||
[{COMMIT_EDITMSG,git-revise-todo}]
|
||||
max_line_length = 80
|
||||
|
||||
42
.github/workflows/staticbuild.yml
vendored
42
.github/workflows/staticbuild.yml
vendored
@@ -12,7 +12,7 @@ env:
|
||||
CMAKE_BUILD_PARALLEL_LEVEL: "4"
|
||||
|
||||
jobs:
|
||||
staticbuilds:
|
||||
staticbuilds-linux:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -29,19 +29,55 @@ jobs:
|
||||
sudo apt install python3-sphinx
|
||||
rustup target add x86_64-unknown-linux-musl
|
||||
rustup target add aarch64-unknown-linux-musl
|
||||
sudo apt install musl-tools crossbuild-essential-arm64 -y
|
||||
sudo apt install musl-tools crossbuild-essential-arm64 python3-pexpect tmux -y
|
||||
- name: Build
|
||||
run: |
|
||||
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" CMAKE_WITH_GETTEXT=0 CC=aarch64-linux-gnu-gcc RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-lgcc -C link-arg=-D_FORTIFY_SOURCE=0" cargo build --release --target aarch64-unknown-linux-musl
|
||||
cargo build --release --target x86_64-unknown-linux-musl
|
||||
- name: Test
|
||||
run: |
|
||||
FISHDIR=target/x86_64-unknown-linux-musl/release/ tests/test_driver.sh tests/test.fish
|
||||
FISHDIR=target/x86_64-unknown-linux-musl/release/ tests/test_driver.sh tests/interactive.fish
|
||||
- name: Compress
|
||||
run: |
|
||||
tar -cazf fish-amd64.tar.xz -C target/x86_64-unknown-linux-musl/release/ fish{,_indent,_key_reader}
|
||||
tar -cazf fish-aarch64.tar.xz -C target/aarch64-unknown-linux-musl/release/ fish{,_indent,_key_reader}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fish
|
||||
name: fish-static-linux
|
||||
path: |
|
||||
fish-amd64.tar.xz
|
||||
fish-aarch64.tar.xz
|
||||
retention-days: 14
|
||||
staticbuilds-macos:
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: dtolnay/rust-toolchain@1.70
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Prepare
|
||||
run: |
|
||||
sudo pip3 install --break-system-packages sphinx
|
||||
rustup target add x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin
|
||||
- name: Build
|
||||
run: |
|
||||
RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target aarch64-apple-darwin
|
||||
RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-apple-darwin
|
||||
- name: Compress
|
||||
run: |
|
||||
tar -cazf fish-macos-aarch64.tar.xz -C target/aarch64-apple-darwin/release/ fish{,_indent,_key_reader}
|
||||
tar -cazf fish-macos-amd64.tar.xz -C target/x86_64-apple-darwin/release/ fish{,_indent,_key_reader}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: fish-static-macos
|
||||
path: |
|
||||
fish-macos-amd64.tar.xz
|
||||
fish-macos-aarch64.tar.xz
|
||||
retention-days: 14
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -102,3 +102,6 @@ target/
|
||||
|
||||
# Generated by clangd
|
||||
/.cache
|
||||
|
||||
# JetBrains editors.
|
||||
.idea/
|
||||
|
||||
@@ -1,10 +1,47 @@
|
||||
fish 4.1.0 (released ???)
|
||||
=========================
|
||||
|
||||
Notable improvements and fixes
|
||||
------------------------------
|
||||
|
||||
Deprecations and removed features
|
||||
---------------------------------
|
||||
|
||||
Scripting improvements
|
||||
----------------------
|
||||
|
||||
Interactive improvements
|
||||
------------------------
|
||||
- Autosuggestions are now also provided in multi-line command lines. Like `ctrl-r`, autosuggestions operate only on the current line.
|
||||
- New feature flag ``buffered-enter-noexec`` with the following effect:
|
||||
when typing a command and :kbd:`enter` while the previous one is still running, the new one will no longer execute immediately. Similarly, keys that are bound to shell commands will be ignored.
|
||||
This mitigates a security issue where a command like ``cat malicious-file.txt`` could write terminal escape codes prompting the terminal to write arbitrary text to fish's standard input.
|
||||
Such a malicious file can still potentially insert arbitrary text into the command line but can no longer execute it directly (:issue:`10987`).
|
||||
|
||||
New or improved bindings
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- :kbd:`ctrl-z` (undo) after executing a command will restore the previous cursor position instead of placing the cursor at the end of the command line.
|
||||
- The OSC 133 prompt marking feature has learned about kitty's ``click_events=1`` flag, which allows moving fish's cursor by clicking.
|
||||
- :kbd:`ctrl-l` no longer clears the screen but only pushes to the terminal's scrollback all text above the prompt (via a new special input function ``scrollback-push``).
|
||||
This feature depends on the terminal advertising via XTGETTCAP support for the ``indn`` and ``cuu`` terminfo capabilities,
|
||||
and on the terminal supporting Synchronized Output (which is used by fish to detect features).
|
||||
If any is missing, the binding falls back to ``clear-screen``.
|
||||
|
||||
Completions
|
||||
^^^^^^^^^^^
|
||||
|
||||
Improved terminal support
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Other improvements
|
||||
------------------
|
||||
|
||||
For distributors
|
||||
----------------
|
||||
|
||||
fish 4.0b1 (released December 17, 2024)
|
||||
=======================================
|
||||
|
||||
Changes since 4.0b1
|
||||
-------------------
|
||||
- :kbd:`ctrl-c` cancels builtin ``read`` again, fixing a regression in the beta.
|
||||
|
||||
These are the draft release notes for fish 4.0.0. Like this release of fish itself, they are in beta and are not complete. Please report any issues you find.
|
||||
|
||||
.. ignore: 751 2037 2037 3017 3018 3162 3299 4770 4865 5284 5991 6981 6996 7172 9332 9439 9440 9442 9452 9469 9480 9482 9520 9536 9541 9542 9544 9554 9556 9559 9561 9563 9566 9567 9568 9573 9575 9576 9579 9585 9586 9588 9589 9591 9592 9593 9594 9599 9600 9603 9607 9608 9612 9613 9615 9616 9619 9621 9625 9626 9630 9636 9637 9638 9641 9642 9643 9653 9654 9658 9661 9666 9671 9673 9688 9725 9726 9729 9735 9739 9745 9746 9751 9754 9765 9767 9768 9771 9777 9778 9786 9816 9818 9821 9839 9845 9856 9859 9861 9863 9864 9867 9869 9873 9874 9879 9881 9893 9894 9896 9902 9916 9923 9925 9927 9928 9930 9947 9948 9950 9952 9962 9963 9966 9968 9980 9981 9984 9990 9991 10040 10061 10090 10101 10102 10108 10114 10115 10121 10128 10129 10143 10145 10146 10161 10173 10174 10175 10179 10180 10181 10182 10184 10185 10186 10188 10195 10198 10200 10201 10204 10210 10214 10219 10220 10222 10223 10227 10228 10232 10235 10237 10241 10243 10244 10245 10246 10251 10254 10260 10263 10267 10268 10270 10272 10276 10277 10278 10279 10281 10288 10290 10291 10293 10305 10306 10307 10308 10309 10316 10317 10321 10327 10328 10329 10330 10336 10338 10340 10342 10345 10346 10347 10348 10349 10353 10354 10355 10356 10357 10358 10360 10366 10368 10370 10371 10372 10373 10377 10379 10381 10388 10389 10390 10395 10398 10400 10403 10404 10407 10408 10409 10411 10412 10415 10417 10418 10427 10429 10434 10438 10439 10440 10441 10442 10443 10445 10446 10448 10450 10451 10452 10456 10457 10462 10463 10464 10466 10467 10471 10473 10474 10479 10481 10485 10486 10487 10490 10491 10492 10494 10499 10500 10503 10505 10507 10508 10509 10510 10511 10512 10513 10518 10519 10520 10524 10528 10529 10530 10538 10541 10542 10547 10548 10549 10555 10560 10562 10564 10565 10568 10569 10572 10573 10574 10575 10578 10580 10582 10583 10588 10591 10594 10595 10596 10609 10622 10623 10627 10628 10634 10635 10636 10637 10640 10646 10647 10649 10650 10652 10653 10654 10655 10657 10659 10662 10664 10667 10669 10670 10674 10679 10681 10685 10686 10687 10688 10689 10697 10698 10702 10707 10708 10712 10713 10716 10718 10719 10721 10726 10727 10728 10731 10760 10762 10763 10767 10770 10775 10776 10778 10779 10782 10784 10789 10792 10795 10796 10801 10812 10817 10825 10836 10839 10844 10845 10847 10851 10858 10863 10864 10873 10874 10880 10885 10891 10894 10907
|
||||
@@ -22,7 +59,7 @@ Notable backwards-incompatible changes
|
||||
- Terminals that fail to ignore unrecognized OSC or CSI sequences may display garbage. We know cool-retro-term and emacs' ansi-term are affected,
|
||||
most mainstream terminals are not.
|
||||
- :kbd:`alt-left` and :kbd:`alt-right` will now move by one argument (which may contain quoted spaces), not just one word like :kbd:`ctrl-left` and :kbd:`ctrl-right` do.
|
||||
- :kbd:`alt-backspace` will delete an entire argument, not just one word (which is :kbd:`ctrl-backspace` now).
|
||||
- :kbd:`alt-backspace` will delete an entire argument, not just one word. The old word behavior has been moved to :kbd:`ctrl-backspace`. If your terminal doesn't support `ctrl-backspace`, consider using :kbd:`ctrl-w`, or :kbd:`alt-b` + :kbd:`alt-d`.
|
||||
- ``random`` will produce different values from previous versions of fish when used with the same seed, and will work more sensibly with small seed numbers.
|
||||
The seed was never guaranteed to give the same result across systems,
|
||||
so we do not expect this to have a large impact (:issue:`9593`).
|
||||
@@ -65,7 +102,7 @@ Notable improvements and fixes
|
||||
|
||||
This build system is experimental; the main build system, using ``cmake``, remains the recommended approach for packaging and installation to a prefix.
|
||||
- A new function ``fish_should_add_to_history`` can be overridden to decide whether a command should be added to the history (:issue:`10302`).
|
||||
- :kbd:`ctrl-c` during command input no longer prints ``^C`` and a new prompt, but merely clears the command line. This restores the behavior from version 2.2. To revert to the old behavior, use ``bind ctrl-c __fish_cancel_commandline`` (:issue:`10213`).
|
||||
- :kbd:`ctrl-c` during command input no longer prints ``^C`` and a new prompt, but merely clears the command line. This restores the behavior from version 2.2. To revert to the old behavior, use ``for mode in (bind --list-modes); bind -M $mode ctrl-c cancel-commandline-traditional; end`` (:issue:`10213`).
|
||||
- Bindings can now mix special input functions and shell commands, so ``bind ctrl-g expand-abbr "commandline -i \n"`` works as expected (:issue:`8186`).
|
||||
- Special input functions run from bindings via ``commandline -f`` are now applied immediately, instead of after the currently executing binding (:issue:`3031`).
|
||||
For example, ``commandline -i foo; commandline | grep foo`` succeeds now.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||
|
||||
@@ -56,7 +56,7 @@ function(CREATE_TARGET target)
|
||||
${Rust_CARGO}
|
||||
build --bin ${target}
|
||||
$<$<CONFIG:Release>:--release>
|
||||
$<$<CONFIG:RelWithDebInfo>:--release>
|
||||
$<$<CONFIG:RelWithDebInfo>:--profile=release-with-debug>
|
||||
--target ${Rust_CARGO_TARGET}
|
||||
--no-default-features
|
||||
${CARGO_FLAGS}
|
||||
@@ -81,8 +81,6 @@ create_target(fish_key_reader)
|
||||
# Set up the docs.
|
||||
include(cmake/Docs.cmake)
|
||||
|
||||
# A helper for running tests.
|
||||
add_executable(fish_test_helper src/fish_test_helper.c)
|
||||
# Set up tests.
|
||||
include(cmake/Tests.cmake)
|
||||
|
||||
|
||||
@@ -126,4 +126,3 @@ enforcement ladder](https://github.com/mozilla/diversity).
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
||||
|
||||
@@ -196,8 +196,13 @@ The tests can be found in three places:
|
||||
- tests/pexpects for interactive tests using `pexpect <https://pexpect.readthedocs.io/en/stable/>`__
|
||||
|
||||
When in doubt, the bulk of the tests should be added as a littlecheck test in tests/checks, as they are the easiest to modify and run, and much faster and more dependable than pexpect tests. The syntax is fairly self-explanatory. It's a fish script with the expected output in ``# CHECK:`` or ``# CHECKERR:`` (for stderr) comments.
|
||||
If your littlecheck test has a specific dependency, use ``# REQUIRE: ...`` with a posix sh script.
|
||||
|
||||
The pexpects are written in python and can simulate input and output to/from a terminal, so they are needed for anything that needs actual interactivity. The runner is in build_tools/pexpect_helper.py, in case you need to modify something there.
|
||||
Tests are run in a temporary $HOME, but that is shared among the tests by default. If you need a temporary directory for your test, you should create one (e.g. with ``mktemp``).
|
||||
|
||||
The pexpects are written in python and can simulate input and output to/from a terminal, so they are needed for anything that needs actual interactivity. The runner is in tests/pexpect_helper.py, in case you need to modify something there.
|
||||
|
||||
If you need a command to do something weird to test something, maybe add it to the ``fish_test_helper`` binary (in tests/fish_test_helper.c), or see if it can already do it.
|
||||
|
||||
Local testing
|
||||
-------------
|
||||
@@ -209,6 +214,15 @@ The tests can be run on your local computer on all operating systems.
|
||||
cmake path/to/fish-shell
|
||||
make test
|
||||
|
||||
Or you can run them on a fish, without involving cmake::
|
||||
|
||||
cargo build
|
||||
FISHDIR=target/debug tests/test_driver.sh tests/test.fish # script tests, the checks
|
||||
FISHDIR=target/debug tests/test_driver.sh tests/interactive.fish # interactive tests, the pexpects
|
||||
|
||||
Here, ``FISHDIR`` refers to a directory with ``fish``, ``fish_indent`` and ``fish_key_reader`` in it.
|
||||
In this example we're in the root of the git repo and have run ``cargo build`` without ``--release``, so it's a debug build.
|
||||
|
||||
Git hooks
|
||||
---------
|
||||
|
||||
|
||||
2
COPYING
2
COPYING
@@ -1,7 +1,7 @@
|
||||
Fish is a smart and user-friendly command line shell.
|
||||
|
||||
Copyright (C) 2005-2009 Axel Liljencrantz
|
||||
Copyright (C) 2009-2024 fish-shell contributors
|
||||
Copyright (C) 2009- fish-shell contributors
|
||||
|
||||
fish is free software.
|
||||
|
||||
|
||||
137
Cargo.lock
generated
137
Cargo.lock
generated
@@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -31,9 +31,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.30"
|
||||
version = "1.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945"
|
||||
checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -54,9 +54,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.14"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -71,19 +71,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -102,12 +89,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.9"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -118,7 +105,6 @@ dependencies = [
|
||||
"cc",
|
||||
"errno",
|
||||
"fish-printf",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"lru",
|
||||
"nix",
|
||||
@@ -150,9 +136,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
@@ -166,15 +152,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
@@ -190,17 +170,11 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.159"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -224,7 +198,7 @@ version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.0",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -365,24 +339,24 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.87"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -404,9 +378,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -440,7 +414,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.79",
|
||||
"syn",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -463,6 +437,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scc"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640"
|
||||
dependencies = [
|
||||
"sdd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -470,26 +453,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "1.0.0"
|
||||
name = "sdd"
|
||||
version = "3.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "538c30747ae860d6fb88330addbbd3e0ddbe46d662d032855596d8a8ca260611"
|
||||
checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9"
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"scc",
|
||||
"serial_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "1.0.0"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "079a83df15f85d89a68d64ae1238f142f172b1fa915d0d76b26a7cba1b659a69"
|
||||
checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -523,20 +512,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
version = "2.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||
checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -563,9 +541,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
@@ -595,16 +573,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
10
Cargo.toml
10
Cargo.toml
@@ -3,6 +3,7 @@ resolver = "2"
|
||||
members = ["printf"]
|
||||
|
||||
[workspace.package]
|
||||
# To build revisions that use Corrosion (those before 2024-01), CMake 3.19 and Rustc 1.78 seem to work.
|
||||
rust-version = "1.70"
|
||||
edition = "2021"
|
||||
|
||||
@@ -10,6 +11,10 @@ edition = "2021"
|
||||
overflow-checks = true
|
||||
lto = true
|
||||
|
||||
[profile.release-with-debug]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.0.0-beta.1"
|
||||
@@ -30,8 +35,7 @@ pcre2 = { git = "https://github.com/fish-shell/rust-pcre2", tag = "0.2.9-utf32",
|
||||
|
||||
bitflags = "2.5.0"
|
||||
errno = "0.3.0"
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.155"
|
||||
libc = "0.2"
|
||||
# lru pulls in hashbrown by default, which uses a faster (though less DoS resistant) hashing algo.
|
||||
# disabling default features uses the stdlib instead, but it doubles the time to rewrite the history
|
||||
# files as of 22 April 2024.
|
||||
@@ -61,7 +65,7 @@ portable-atomic = { version = "1", default-features = false, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = { version = "1.0.0", default-features = false }
|
||||
serial_test = { version = "3", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.94"
|
||||
|
||||
@@ -16,4 +16,3 @@ WORKDIR /src
|
||||
RUN cmake3 . &&\
|
||||
make &&\
|
||||
make install
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ Dependencies
|
||||
Compiling fish requires:
|
||||
|
||||
- Rust (version 1.70 or later)
|
||||
- CMake (version 3.5 or later)
|
||||
- CMake (version 3.15 or later)
|
||||
- a C compiler (for system feature detection and the test helper binary)
|
||||
- PCRE2 (headers and libraries) - optional, this will be downloaded if missing
|
||||
- gettext (headers and libraries) - optional, for translation support
|
||||
|
||||
@@ -8,5 +8,4 @@ for file in *.fish
|
||||
echo FAILING FILE $file
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
58
build.rs
58
build.rs
@@ -29,6 +29,11 @@ fn main() {
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// Some build info
|
||||
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_PROFILE", &env::var("PROFILE").unwrap());
|
||||
|
||||
let version = &get_version(&env::current_dir().unwrap());
|
||||
// Per https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script,
|
||||
// the source directory is the current working directory of the build script
|
||||
@@ -222,7 +227,7 @@ fn has_small_stack(_: &Target) -> Result<bool, Box<dyn Error>> {
|
||||
}
|
||||
|
||||
fn setup_paths() {
|
||||
fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
fn get_path(name: &str, default: &str, onvar: &Path) -> PathBuf {
|
||||
let mut var = PathBuf::from(env::var(name).unwrap_or(default.to_string()));
|
||||
if var.is_relative() {
|
||||
var = onvar.join(var);
|
||||
@@ -245,7 +250,7 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
rsconf::rebuild_if_env_changed("PREFIX");
|
||||
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
|
||||
|
||||
let datadir = get_path("DATADIR", "share/", prefix.clone());
|
||||
let datadir = get_path("DATADIR", "share/", &prefix);
|
||||
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("DATADIR");
|
||||
|
||||
@@ -256,7 +261,7 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
};
|
||||
rsconf::set_env_value("DATADIR_SUBDIR", datadir_subdir);
|
||||
|
||||
let bindir = get_path("BINDIR", "bin/", prefix.clone());
|
||||
let bindir = get_path("BINDIR", "bin/", &prefix);
|
||||
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("BINDIR");
|
||||
|
||||
@@ -265,16 +270,16 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
|
||||
// If we get our prefix from $HOME, we should use the system's /etc/
|
||||
// ~/.local/share/etc/ makes no sense
|
||||
if prefix_from_home { "/etc/" } else { "etc/" },
|
||||
datadir.clone(),
|
||||
&datadir,
|
||||
);
|
||||
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("SYSCONFDIR");
|
||||
|
||||
let localedir = get_path("LOCALEDIR", "locale/", datadir.clone());
|
||||
let localedir = get_path("LOCALEDIR", "locale/", &datadir);
|
||||
rsconf::set_env_value("LOCALEDIR", localedir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("LOCALEDIR");
|
||||
|
||||
let docdir = get_path("DOCDIR", "doc/fish", datadir.clone());
|
||||
let docdir = get_path("DOCDIR", "doc/fish", &datadir);
|
||||
rsconf::set_env_value("DOCDIR", docdir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("DOCDIR");
|
||||
}
|
||||
@@ -287,7 +292,7 @@ fn get_version(src_dir: &Path) -> String {
|
||||
return var;
|
||||
}
|
||||
|
||||
let path = PathBuf::from(src_dir).join("version");
|
||||
let path = src_dir.join("version");
|
||||
if let Ok(strver) = read_to_string(path) {
|
||||
return strver.to_string();
|
||||
}
|
||||
@@ -316,17 +321,36 @@ fn get_version(src_dir: &Path) -> String {
|
||||
// or because it refused (safe.directory applies to `git describe`!)
|
||||
// So we read the SHA ourselves.
|
||||
fn get_git_hash() -> Result<String, Box<dyn std::error::Error>> {
|
||||
let gitdir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".git");
|
||||
let gitdir = Path::new(env!("CARGO_MANIFEST_DIR")).join(".git");
|
||||
let jjdir = Path::new(env!("CARGO_MANIFEST_DIR")).join(".jj");
|
||||
let commit_id = if gitdir.exists() {
|
||||
// .git/HEAD contains ref: refs/heads/branch
|
||||
let headpath = gitdir.join("HEAD");
|
||||
let headstr = read_to_string(headpath)?;
|
||||
let headref = headstr.split(' ').collect::<Vec<_>>()[1].trim();
|
||||
|
||||
// .git/HEAD contains ref: refs/heads/branch
|
||||
let headpath = gitdir.join("HEAD");
|
||||
let headstr = read_to_string(headpath)?;
|
||||
let headref = headstr.split(' ').collect::<Vec<_>>()[1].trim();
|
||||
|
||||
// .git/refs/heads/branch contains the SHA
|
||||
let refpath = gitdir.join(headref);
|
||||
// Shorten to 9 characters (what git describe does currently)
|
||||
let refstr = &read_to_string(refpath)?[0..9];
|
||||
// .git/refs/heads/branch contains the SHA
|
||||
let refpath = gitdir.join(headref);
|
||||
// Shorten to 9 characters (what git describe does currently)
|
||||
read_to_string(refpath)?
|
||||
} else if jjdir.exists() {
|
||||
let output = Command::new("jj")
|
||||
.args([
|
||||
"log",
|
||||
"--revisions",
|
||||
"@",
|
||||
"--no-graph",
|
||||
"--ignore-working-copy",
|
||||
"--template",
|
||||
"commit_id",
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
String::from_utf8_lossy(&output.stdout).to_string()
|
||||
} else {
|
||||
return Err("did not find either of .git or .jj".into());
|
||||
};
|
||||
let refstr = &commit_id[0..9];
|
||||
let refstr = refstr.trim();
|
||||
|
||||
let version = env!("CARGO_PKG_VERSION").to_owned();
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
code, tt {
|
||||
font-family: ui-monospace, Menlo, monospace;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -29,7 +29,7 @@ add_custom_target(sphinx-docs
|
||||
|
||||
# sphinx-manpages needs the fish_indent binary for the version number
|
||||
add_custom_target(sphinx-manpages
|
||||
env FISH_BUILD_VERSION_FILE="${CMAKE_CURRENT_BINARY_DIR}/${FBVF}"
|
||||
env FISH_BUILD_VERSION_FILE=${CMAKE_CURRENT_BINARY_DIR}/${FBVF}
|
||||
${SPHINX_EXECUTABLE}
|
||||
-j auto
|
||||
-q -b man
|
||||
|
||||
@@ -22,7 +22,7 @@ else()
|
||||
set(rust_target_dir "${FISH_RUST_BUILD_DIR}/${Rust_CARGO_HOST_TARGET}")
|
||||
endif()
|
||||
|
||||
set(rust_profile $<IF:$<CONFIG:Debug>,debug,release>)
|
||||
set(rust_profile $<IF:$<CONFIG:Debug>,debug,$<IF:$<CONFIG:RelWithDebInfo>,release-with-debug,release>>)
|
||||
set(rust_debugflags "$<$<CONFIG:Debug>:-g>$<$<CONFIG:RelWithDebInfo>:-g>")
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ set(VARS_FOR_CARGO
|
||||
"PREFIX=${CMAKE_INSTALL_PREFIX}"
|
||||
# Temporary hack to propogate CMake flags/options to build.rs.
|
||||
"CMAKE_WITH_GETTEXT=${CMAKE_WITH_GETTEXT}"
|
||||
# Cheesy so we can tell cmake was used to build
|
||||
"CMAKE=1"
|
||||
"DOCDIR=${CMAKE_INSTALL_FULL_DOCDIR}"
|
||||
"DATADIR=${CMAKE_INSTALL_FULL_DATADIR}"
|
||||
"SYSCONFDIR=${CMAKE_INSTALL_FULL_SYSCONFDIR}"
|
||||
|
||||
@@ -71,12 +71,6 @@ if(NOT FISH_IN_TREE_BUILD)
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
# Copy littlecheck.py
|
||||
configure_file(build_tools/littlecheck.py littlecheck.py COPYONLY)
|
||||
|
||||
# Copy pexpect_helper.py
|
||||
configure_file(build_tools/pexpect_helper.py pexpect_helper.py COPYONLY)
|
||||
|
||||
# Suppress generating Xcode schemes for all tests, there's too many.
|
||||
set(CMAKE_XCODE_GENERATE_SCHEME 0)
|
||||
|
||||
@@ -95,22 +89,19 @@ add_custom_target(tests_buildroot_target
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_INSTALL_DIR}
|
||||
COMMAND env DESTDIR=${TEST_INSTALL_DIR} ${CMAKE_COMMAND}
|
||||
--build ${CMAKE_CURRENT_BINARY_DIR} --target install
|
||||
# Put fish_test_helper there too:
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/fish_test_helper
|
||||
${TEST_INSTALL_DIR}/${CMAKE_INSTALL_PREFIX}/bin
|
||||
# Also symlink fish to where the tests expect it to be:
|
||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
||||
${TEST_INSTALL_DIR}/${CMAKE_INSTALL_PREFIX}
|
||||
${TEST_ROOT_DIR}
|
||||
DEPENDS fish fish_test_helper)
|
||||
DEPENDS fish)
|
||||
|
||||
FILE(GLOB FISH_CHECKS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/checks/*.fish)
|
||||
foreach(CHECK ${FISH_CHECKS})
|
||||
get_filename_component(CHECK_NAME ${CHECK} NAME)
|
||||
get_filename_component(CHECK ${CHECK} NAME_WE)
|
||||
add_test(NAME ${CHECK_NAME}
|
||||
COMMAND sh ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.sh
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tests/test.fish ${CHECK}
|
||||
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
|
||||
checks/${CHECK}.fish
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
|
||||
)
|
||||
set_tests_properties(${CHECK_NAME} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
|
||||
@@ -122,8 +113,8 @@ FILE(GLOB PEXPECTS CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/tests/pexpects/*.py)
|
||||
foreach(PEXPECT ${PEXPECTS})
|
||||
get_filename_component(PEXPECT ${PEXPECT} NAME)
|
||||
add_test(NAME ${PEXPECT}
|
||||
COMMAND sh ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.sh
|
||||
${CMAKE_CURRENT_BINARY_DIR}/tests/interactive.fish ${PEXPECT}
|
||||
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/test_driver.py ${CMAKE_CURRENT_BINARY_DIR}
|
||||
pexpects/${PEXPECT}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests
|
||||
)
|
||||
set_tests_properties(${PEXPECT} PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})
|
||||
|
||||
29
debian/control
vendored
29
debian/control
vendored
@@ -3,10 +3,16 @@ Section: shells
|
||||
Priority: optional
|
||||
Maintainer: ridiculous_fish <corydoras@ridiculousfish.com>
|
||||
Uploaders: David Adam <zanchey@ucc.gu.uwa.edu.au>
|
||||
Build-Depends: debhelper (>= 12), cmake (>= 3.19.0) | cmake-mozilla (>= 3.19.0), gettext,
|
||||
rustc (>= 1.70), cargo (>= 0.66) | cargo-mozilla (>= 0.66), libpcre2-dev,
|
||||
Build-Depends: debhelper (>= 12),
|
||||
cargo (>= 0.66) | cargo-mozilla (>= 0.66),
|
||||
cmake (>= 3.15.0) | cmake-mozilla (>= 3.15.0),
|
||||
gettext,
|
||||
libpcre2-dev,
|
||||
rustc (>= 1.70),
|
||||
# Test dependencies
|
||||
locales-all, ncurses-base, python3
|
||||
locales-all,
|
||||
ncurses-base,
|
||||
python3
|
||||
Standards-Version: 4.1.5
|
||||
Homepage: https://fishshell.com/
|
||||
Vcs-Git: https://github.com/fish-shell/fish-shell.git
|
||||
@@ -14,8 +20,21 @@ Vcs-Browser: https://github.com/fish-shell/fish-shell
|
||||
|
||||
Package: fish
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}, file, gettext-base, man-db, ncurses-base, procps,
|
||||
python3 (>=3.5)
|
||||
# for col and lock
|
||||
Depends: bsdextrautils,
|
||||
file,
|
||||
# for the gettext command
|
||||
gettext-base,
|
||||
# for nroff and preconv
|
||||
groff-base,
|
||||
man-db,
|
||||
# for terminal definitions
|
||||
ncurses-base,
|
||||
# for kill
|
||||
procps,
|
||||
python3 (>=3.5),
|
||||
${misc:Depends},
|
||||
${shlibs:Depends}
|
||||
Conflicts: fish-common
|
||||
Recommends: xsel (>=1.2.0)
|
||||
Suggests: xdg-utils
|
||||
|
||||
4
debian/copyright
vendored
4
debian/copyright
vendored
@@ -5,12 +5,12 @@ Source: https://fishshell.com/
|
||||
|
||||
Files: *
|
||||
Copyright: 2005-2009 Axel Liljencrantz <axel@liljencrantz.se>
|
||||
2009-2024 fish-shell contributors
|
||||
2009- fish-shell contributors
|
||||
License: GPL-2
|
||||
|
||||
Files: doc_src/python_docs_theme/*
|
||||
Copyright: 2001-2017 Python Software Foundation
|
||||
2020-2024 fish-shell contributors
|
||||
2020- fish-shell contributors
|
||||
License: Python
|
||||
|
||||
Files: share/tools/web_config/js/alpine.js
|
||||
|
||||
@@ -19,4 +19,4 @@ allow = [
|
||||
|
||||
[sources.allow-org]
|
||||
# 1 or more github.com organizations to allow git sources for
|
||||
github = ["fish-shell"]
|
||||
github = ["fish-shell"]
|
||||
|
||||
@@ -53,7 +53,7 @@ Combining these features, it is possible to create custom syntaxes, where a regu
|
||||
|
||||
> abbr > ~/.config/fish/conf.d/myabbrs.fish
|
||||
|
||||
This will save all your abbrevations in "myabbrs.fish", overwriting the whole file so it doesn't leave any duplicates,
|
||||
This will save all your abbreviations in "myabbrs.fish", overwriting the whole file so it doesn't leave any duplicates,
|
||||
or restore abbreviations you had erased.
|
||||
Of course any functions will have to be saved separately, see :doc:`funcsave <funcsave>`.
|
||||
|
||||
|
||||
@@ -171,7 +171,10 @@ The following special input functions are available:
|
||||
make the current word begin with a capital letter
|
||||
|
||||
``clear-screen``
|
||||
clears the screen and redraws the prompt. if the terminal doesn't support clearing the screen it is the same as ``repaint``.
|
||||
clears the screen and redraws the prompt.
|
||||
|
||||
``scrollback-push``
|
||||
pushes earlier output to the terminal scrollback, positioning the prompt at the top.
|
||||
|
||||
``complete``
|
||||
guess the remainder of the current token
|
||||
@@ -259,7 +262,7 @@ The following special input functions are available:
|
||||
search the history for the next matching argument
|
||||
|
||||
``forward-jump`` and ``backward-jump``
|
||||
read another character and jump to its next occurence after/before the cursor
|
||||
read another character and jump to its next occurrence after/before the cursor
|
||||
|
||||
``forward-jump-till`` and ``backward-jump-till``
|
||||
jump to right *before* the next occurrence
|
||||
@@ -269,7 +272,7 @@ The following special input functions are available:
|
||||
|
||||
``jump-to-matching-bracket``
|
||||
jump to matching bracket if the character under the cursor is bracket;
|
||||
otherwise, jump to the next occurence of *any right* bracket after the cursor.
|
||||
otherwise, jump to the next occurrence of *any right* bracket after the cursor.
|
||||
The following brackets are considered: ``([{}])``
|
||||
|
||||
``jump-till-matching-bracket``
|
||||
@@ -292,7 +295,7 @@ The following special input functions are available:
|
||||
move the selected text to the killring
|
||||
|
||||
``kill-whole-line``
|
||||
move the line (including the following newline) to the killring. If the line is the last line, its preceeding newline is also removed
|
||||
move the line (including the following newline) to the killring. If the line is the last line, its preceding newline is also removed
|
||||
|
||||
``kill-inner-line``
|
||||
move the line (without the following newline) to the killring
|
||||
|
||||
@@ -116,7 +116,7 @@ The following options output metadata about the commandline state:
|
||||
**--is-valid**
|
||||
Returns true when the commandline is syntactically valid and complete.
|
||||
If it is, it would be executed when the ``execute`` bind function is called.
|
||||
If the commandline is incomplete, return 2, if erroneus, return 1.
|
||||
If the commandline is incomplete, return 2, if erroneous, return 1.
|
||||
|
||||
**--showing-suggestion**
|
||||
Evaluates to true (i.e. returns 0) when the shell is currently showing an automatic history completion/suggestion, available to be consumed via one of the `forward-` bindings.
|
||||
|
||||
@@ -40,9 +40,11 @@ The following options are available:
|
||||
**-i** or **--interactive**
|
||||
The shell is interactive.
|
||||
|
||||
**--install**
|
||||
**--install[=PATH]**
|
||||
When built as self-installable (via cargo), this will unpack fish's datafiles and place them in ~/.local/share/fish/install/.
|
||||
Fish will also ask to do this automatically when run interactively.
|
||||
If PATH is given, fish will install itself into a relocatable directory tree rooted at that path.
|
||||
That means it will install the datafiles to PATH/share/fish and copy itself to PATH/bin/fish.
|
||||
|
||||
**-l** or **--login**
|
||||
Act as if invoked as a login shell.
|
||||
|
||||
@@ -29,6 +29,7 @@ Synopsis
|
||||
status job-control CONTROL_TYPE
|
||||
status features
|
||||
status test-feature FEATURE
|
||||
status buildinfo
|
||||
|
||||
Description
|
||||
-----------
|
||||
@@ -97,6 +98,10 @@ The following operations (subcommands) are available:
|
||||
**test-feature** *FEATURE*
|
||||
Returns 0 when FEATURE is enabled, 1 if it is disabled, and 2 if it is not recognized.
|
||||
|
||||
**buildinfo**
|
||||
This prints information on how fish was build - which architecture, which build system or profile was used, etc.
|
||||
This is mainly useful for debugging.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ Description
|
||||
|
||||
.. BEGIN DESCRIPTION
|
||||
|
||||
``string escape`` escapes each *STRING* in one of three ways. The first is **--style=script**. This is the default. It alters the string such that it can be passed back to ``eval`` to produce the original argument again. By default, all special characters are escaped, and quotes are used to simplify the output when possible. If **-n** or **--no-quoted** is given, the simplifying quoted format is not used. Exit status: 0 if at least one string was escaped, or 1 otherwise.
|
||||
``string escape`` escapes each *STRING* in one of several ways.
|
||||
|
||||
**--style=script** (default) alters the string such that it can be passed back to ``eval`` to produce the original argument again. By default, all special characters are escaped, and quotes are used to simplify the output when possible. If **-n** or **--no-quoted** is given, the simplifying quoted format is not used. Exit status: 0 if at least one string was escaped, or 1 otherwise.
|
||||
|
||||
**--style=var** ensures the string can be used as a variable name by hex encoding any non-alphanumeric characters. The string is first converted to UTF-8 before being encoded.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ Description
|
||||
|
||||
.. BEGIN DESCRIPTION
|
||||
|
||||
``string repeat`` repeats the *STRING* **-n** or **--count** times. The **-m** or **--max** option will limit the number of outputted characters (excluding the newline). This option can be used by itself or in conjunction with **--count**. If both **--count** and **--max** are present, max char will be outputed unless the final repeated string size is less than max, in that case, the string will repeat until count has been reached. Both **--count** and **--max** will accept a number greater than or equal to zero, in the case of zero, nothing will be outputed. The first argument is interpreted as *COUNT* if **--count** or **--max** are not explicilty specified. If **-N** or **--no-newline** is given, the output won't contain a newline character at the end. Exit status: 0 if yielded string is not empty, 1 otherwise.
|
||||
``string repeat`` repeats the *STRING* **-n** or **--count** times. The **-m** or **--max** option will limit the number of outputted characters (excluding the newline). This option can be used by itself or in conjunction with **--count**. If both **--count** and **--max** are present, max char will be outputted unless the final repeated string size is less than max, in that case, the string will repeat until count has been reached. Both **--count** and **--max** will accept a number greater than or equal to zero, in the case of zero, nothing will be outputted. The first argument is interpreted as *COUNT* if **--count** or **--max** are not explicitly specified. If **-N** or **--no-newline** is given, the output won't contain a newline character at the end. Exit status: 0 if yielded string is not empty, 1 otherwise.
|
||||
|
||||
.. END DESCRIPTION
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ If **-q** or **--quiet** is given, ``string shorten`` only runs for the return v
|
||||
|
||||
The default ellipsis is ``…``. If fish thinks your system is incapable because of your locale, it will use ``...`` instead.
|
||||
|
||||
The return value is 0 if any shortening occured, 1 otherwise.
|
||||
The return value is 0 if any shortening occurred, 1 otherwise.
|
||||
|
||||
.. END DESCRIPTION
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ highlight_language = "fish-docs-samples"
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "fish-shell"
|
||||
copyright = "2024, fish-shell developers"
|
||||
copyright = "fish-shell developers"
|
||||
author = "fish-shell developers"
|
||||
issue_url = "https://github.com/fish-shell/fish-shell/issues"
|
||||
|
||||
|
||||
@@ -327,8 +327,12 @@ Some bindings are common across Emacs and vi mode, because they aren't text edit
|
||||
|
||||
- :kbd:`alt-d` or :kbd:`ctrl-delete` moves the next word to the :ref:`killring`.
|
||||
|
||||
- :kbd:`alt-d` lists the directory history if the command line is empty.
|
||||
|
||||
- :kbd:`alt-delete` moves the next argument to the :ref:`killring`.
|
||||
|
||||
- :kbd:`shift-delete` removes the current history item or autosuggestion from the command history.
|
||||
|
||||
- :kbd:`alt-h` (or :kbd:`f1`) shows the manual page for the current command, if one exists.
|
||||
|
||||
- :kbd:`alt-l` lists the contents of the current directory, unless the cursor is over a directory argument, in which case the contents of that directory will be listed.
|
||||
@@ -382,7 +386,7 @@ To enable emacs mode, use :doc:`fish_default_key_bindings <cmds/fish_default_key
|
||||
|
||||
- :kbd:`ctrl-z`, :kbd:`ctrl-_` (:kbd:`ctrl-/` on some terminals) undo the most recent edit of the line.
|
||||
|
||||
- :kbd:`alt-/` reverts the most recent undo.
|
||||
- :kbd:`alt-/` or :kbd:`ctrl-shift-z` reverts the most recent undo.
|
||||
|
||||
- :kbd:`ctrl-r` opens the history in a pager. This will show history entries matching the search, a few at a time. Pressing :kbd:`ctrl-r` again will search older entries, pressing :kbd:`ctrl-s` (that otherwise toggles pager search) will go to newer entries. The search bar will always be selected.
|
||||
|
||||
|
||||
@@ -870,7 +870,7 @@ but if you need multiple or the command doesn't read from standard input, "proce
|
||||
|
||||
This creates a temporary file, stores the output of the command in that file and prints the filename, so it is given to the outer command.
|
||||
|
||||
Fish has a default limit of 100 MiB on the data it will read in a command sustitution. If that limit is reached the command (all of it, not just the command substitution - the outer command won't be executed at all) fails and ``$status`` is set to 122. This is so command substitutions can't cause the system to go out of memory, because typically your operating system has a much lower limit, so reading more than that would be useless and harmful. This limit can be adjusted with the ``fish_read_limit`` variable (`0` meaning no limit). This limit also affects the :doc:`read <cmds/read>` command.
|
||||
Fish has a default limit of 100 MiB on the data it will read in a command substitution. If that limit is reached the command (all of it, not just the command substitution - the outer command won't be executed at all) fails and ``$status`` is set to 122. This is so command substitutions can't cause the system to go out of memory, because typically your operating system has a much lower limit, so reading more than that would be useless and harmful. This limit can be adjusted with the ``fish_read_limit`` variable (`0` meaning no limit). This limit also affects the :doc:`read <cmds/read>` command.
|
||||
|
||||
.. [#] One exception: Setting ``$IFS`` to empty will disable line splitting. This is deprecated, use :doc:`string split <cmds/string-split>` instead.
|
||||
|
||||
@@ -1845,7 +1845,7 @@ The "locale" of a program is its set of language and regional settings that depe
|
||||
|
||||
.. envvar:: LC_MONETARY
|
||||
|
||||
Determines currency, how it is formated, and the symbols used.
|
||||
Determines currency, how it is formatted, and the symbols used.
|
||||
|
||||
.. envvar:: LC_NUMERIC
|
||||
|
||||
@@ -2024,6 +2024,7 @@ You can see the current list of features via ``status features``::
|
||||
ampersand-nobg-in-token on 3.4 & only backgrounds if followed by a separating character
|
||||
remove-percent-self off 3.8 %self is no longer expanded (use $fish_pid)
|
||||
test-require-arg off 3.8 builtin test requires an argument
|
||||
buffered-enter-noexec off 4.1 enter typed while executing will not execute
|
||||
|
||||
Here is what they mean:
|
||||
|
||||
@@ -2033,6 +2034,7 @@ Here is what they mean:
|
||||
- ``ampersand-nobg-in-token`` was introduced in fish 3.4. It makes it so a ``&`` i no longer interpreted as the backgrounding operator in the middle of a token, so dealing with URLs becomes easier. Either put spaces or a semicolon after the ``&``. This is recommended formatting anyway, and ``fish_indent`` will have done it for you already.
|
||||
- ``remove-percent-self`` turns off the special ``%self`` expansion. It was introduced in 3.8. To get fish's pid, you can use the :envvar:`fish_pid` variable.
|
||||
- ``test-require-arg`` removes :doc:`builtin test <cmds/test>`'s one-argument form (``test "string"``. It was introduced in 3.8. To test if a string is non-empty, use ``test -n "string"``. If disabled, any call to ``test`` that would change sends a :ref:`debug message <debugging-fish>` of category "deprecated-test", so starting fish with ``fish --debug=deprecated-test`` can be used to find offending calls.
|
||||
- ``buffered-enter-noexec`` typing enter during command execution will insert a newline into the next commandline instead of executing it.
|
||||
|
||||
|
||||
These changes are introduced off by default. They can be enabled on a per session basis::
|
||||
|
||||
@@ -6,7 +6,7 @@ License
|
||||
License for fish
|
||||
----------------
|
||||
|
||||
``fish`` Copyright © 2005-2009 Axel Liljencrantz, 2009-2024 fish-shell contributors. ``fish`` is released under the GNU General Public License, version 2.
|
||||
``fish`` Copyright © 2005-2009 Axel Liljencrantz, 2009- fish-shell contributors. ``fish`` is released under the GNU General Public License, version 2.
|
||||
|
||||
``fish`` includes other code licensed under the GNU General Public License, version 2, including GNU ``printf``.
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ body {
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related {
|
||||
@@ -71,12 +70,10 @@ div.related h3 {
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
width: 230px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
@@ -113,7 +110,6 @@ div.sphinxsidebar ul {
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
color: var(--secondary-link-color);
|
||||
margin: 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@@ -548,10 +544,6 @@ div.sphinxsidebar ul {
|
||||
}
|
||||
|
||||
|
||||
div.bodywrapper {
|
||||
margin-left: 230px;
|
||||
}
|
||||
|
||||
aside.footnote > .label {
|
||||
display: inline;
|
||||
}
|
||||
@@ -565,19 +557,36 @@ div.documentwrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.document {
|
||||
display: grid;
|
||||
grid-template: 1fr min-content / 12rem minmax(0,1fr);
|
||||
}
|
||||
|
||||
/* On screens that are less than 700px wide remove anything non-essential
|
||||
- the sidebar, the gradient background, ... */
|
||||
@media screen and (max-width: 700px) {
|
||||
div.document {
|
||||
display: grid;
|
||||
grid-template: 30vh min-content / 100%;
|
||||
}
|
||||
div.sphinxsidebar {
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
/* To separate the "side"bar from the content below */
|
||||
border-bottom: 1px solid;
|
||||
border-color: var(--sidebar-border-color);
|
||||
}
|
||||
div.bodywrapper {
|
||||
|
||||
/* Reduce margins to save space */
|
||||
div.sphinxsidebar ul ul, div.bodywrapper, div.content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
flex-basis: content;
|
||||
@@ -585,14 +594,15 @@ div.documentwrapper {
|
||||
}
|
||||
div.sphinxsidebarwrapper {
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
justify-content: space-between;
|
||||
}
|
||||
div.sphinxsidebarwrapper > h3:nth-child(5) {
|
||||
display: none;
|
||||
}
|
||||
div#searchbox {
|
||||
#searchbox {
|
||||
display: none !important;
|
||||
}
|
||||
div.content {margin-left: 0;}
|
||||
div.body {
|
||||
padding: 1rem;
|
||||
}
|
||||
@@ -613,6 +623,9 @@ div.documentwrapper {
|
||||
|
||||
/* On print media remove anything non-essential. */
|
||||
@media print {
|
||||
div.document {
|
||||
display: block;
|
||||
}
|
||||
.inline-search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -36,5 +36,4 @@ WORKDIR /home/fishuser
|
||||
|
||||
COPY fish_run_tests.sh /
|
||||
|
||||
|
||||
CMD /fish_run_tests.sh
|
||||
|
||||
@@ -38,5 +38,4 @@ WORKDIR /home/fishuser
|
||||
|
||||
COPY fish_run_tests.sh /
|
||||
|
||||
|
||||
CMD /fish_run_tests.sh
|
||||
|
||||
@@ -35,5 +35,4 @@ WORKDIR /home/fishuser
|
||||
|
||||
COPY fish_run_tests.sh /
|
||||
|
||||
|
||||
CMD /fish_run_tests.sh
|
||||
|
||||
@@ -14,7 +14,7 @@ BuildRequires: cargo gettext gcc xz pcre2-devel
|
||||
BuildRequires: rust >= 1.70
|
||||
# Packaging guidelines say to use a BuildRequires: rust-packaging, but it adds no value for our package
|
||||
|
||||
BuildRequires: cmake >= 3.19
|
||||
BuildRequires: cmake >= 3.15
|
||||
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: update-desktop-files
|
||||
|
||||
@@ -6,4 +6,3 @@
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ fn smoke() {
|
||||
#[test]
|
||||
fn test_format_string_str() {
|
||||
let mut s: &str = "hello%world%%%%%";
|
||||
assert_eq!(s.is_empty(), false);
|
||||
assert!(!s.is_empty());
|
||||
for (idx, c) in s.char_indices() {
|
||||
assert_eq!(s.at(idx), Some(c));
|
||||
}
|
||||
@@ -98,7 +98,7 @@ fn test_format_string_str() {
|
||||
assert_eq!(s.take_literal(&mut buffer), "world%%");
|
||||
|
||||
s.advance_by(1); // advancing over one more %
|
||||
assert_eq!(s.is_empty(), true); // remaining content is empty
|
||||
assert!(s.is_empty()); // remaining content is empty
|
||||
}
|
||||
|
||||
#[cfg(feature = "widestring")]
|
||||
@@ -122,7 +122,7 @@ fn test_format_string_wstr() {
|
||||
assert_eq!(s.take_literal(&mut buffer), "world%%");
|
||||
|
||||
s.advance_by(1); // advancing over one more %
|
||||
assert_eq!(s.is_empty(), true); // remaining content is empty
|
||||
assert!(s.is_empty()); // remaining content is empty
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
1
share/completions/!.fish
Normal file
1
share/completions/!.fish
Normal file
@@ -0,0 +1 @@
|
||||
complete ! --wraps not
|
||||
1
share/completions/[.fish
Normal file
1
share/completions/[.fish
Normal file
@@ -0,0 +1 @@
|
||||
complete [ --wraps test
|
||||
@@ -2,7 +2,7 @@
|
||||
complete -c apt-build -l help -d "Display help and exit"
|
||||
complete -f -c apt-build -a update -d "Update list of packages"
|
||||
complete -f -c apt-build -a upgrade -d "Upgrade packages"
|
||||
complete -f -c apt-bulid -a world -d "Rebuild your system"
|
||||
complete -f -c apt-build -a world -d "Rebuild your system"
|
||||
complete -x -c apt-build -a install -d "Build and install a new package"
|
||||
complete -x -c apt-build -a source -d "Download and extract a source"
|
||||
complete -x -c apt-build -a info -d "Info on a package"
|
||||
|
||||
12
share/completions/batsh.fish
Normal file
12
share/completions/batsh.fish
Normal file
@@ -0,0 +1,12 @@
|
||||
set -l command batsh
|
||||
|
||||
complete -c $command -f
|
||||
|
||||
complete -c $command -s h -l help \
|
||||
-a 'pager\tdefault plain groff' \
|
||||
-d 'Show help'
|
||||
|
||||
complete -c $command -s v -l version -d 'Show version'
|
||||
|
||||
complete -c $command \
|
||||
-a 'bash\t"Compile to Bash" batsh\t"Format file" winbat\t"Compile to Batch"'
|
||||
@@ -72,7 +72,7 @@ function __fish_bind_complete
|
||||
printf '%sshift-\tShift modifier…\n' $prefix
|
||||
set -l key_names minus comma backspace delete escape \
|
||||
enter up down left right pageup pagedown home end insert tab \
|
||||
space f(seq 12)
|
||||
space menu printscreen f(seq 12)
|
||||
printf '%s\tNamed key\n' $prefix$key_names
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
set -l commands status install update remove is-installed random-seed systemd-efi-options reboot-to-firmware list set-default set-oneshot
|
||||
set -l commands status install update remove is-installed random-seed systemd-efi-options reboot-to-firmware list set-default set-oneshot set-timeout set-timeout-oneshot
|
||||
|
||||
complete -c bootctl -f
|
||||
complete -c bootctl -n "not __fish_seen_subcommand_from $commands" -a status -d 'Show status of EFI variables'
|
||||
@@ -13,6 +13,8 @@ complete -c bootctl -n "__fish_seen_subcommand_from reboot-to-firmware" -a 'true
|
||||
complete -c bootctl -n "not __fish_seen_subcommand_from $commands" -a list -d 'List boot loader entries'
|
||||
complete -c bootctl -n "not __fish_seen_subcommand_from $commands" -a set-default -d 'Set default boot loader entry'
|
||||
complete -c bootctl -n "not __fish_seen_subcommand_from $commands" -a set-oneshot -d 'Set default boot loader entry (Once)'
|
||||
complete -c bootctl -n "not __fish_seen_subcommand_from $commands" -a set-timeout -d 'Set default boot loader timeout'
|
||||
complete -c bootctl -n "not __fish_seen_subcommand_from $commands" -a set-timeout-oneshot -d 'Set default boot loader timeout (Once)'
|
||||
|
||||
complete -c bootctl -s h -l help -d 'Show this help'
|
||||
complete -c bootctl -l version -d 'Print version'
|
||||
|
||||
62
share/completions/btrbk.fish
Normal file
62
share/completions/btrbk.fish
Normal file
@@ -0,0 +1,62 @@
|
||||
# Filter Completion
|
||||
function __fish_btrbk_complete_filter
|
||||
btrbk list config --format col:h:snapshot_name,source_subvolume,target_url | string replace -r '([^ ]+)\s+([^ ]+)\s+([^ ]*)' '$1\t$2 -> $3'
|
||||
end
|
||||
|
||||
# options with arguments
|
||||
complete -c btrbk -l format -x -d 'Change output format' -a "table long raw"
|
||||
complete -c btrbk -l loglevel -s l -x -d 'Set logging level' -a "error warn info debug trace"
|
||||
complete -c btrbk -l exclude -x -d 'Exclude configured sections' -a "(__fish_btrbk_complete_filter)"
|
||||
complete -c btrbk -l override -x -d 'Globally override a configuration option'
|
||||
|
||||
# options with file completion
|
||||
complete -c btrbk -l config -r -s c -d 'Specify configuration file'
|
||||
complete -c btrbk -l lockfile -r -d 'Create and check lockfile'
|
||||
|
||||
# options without arguments
|
||||
complete -c btrbk -l help -s h -d 'Display this help message'
|
||||
complete -c btrbk -l version -d 'Display version information'
|
||||
complete -c btrbk -l dry-run -s n -d 'Perform a trial run with no changes made'
|
||||
complete -c btrbk -l preserve -s p -d 'Preserve all (do not delete anything)'
|
||||
complete -c btrbk -l preserve-snapshots -d 'Preserve snapshots (do not delete snapshots)'
|
||||
complete -c btrbk -l preserve-backups -d 'Preserve backups (do not delete backups)'
|
||||
complete -c btrbk -l wipe -d 'Delete all but latest snapshots'
|
||||
complete -c btrbk -l verbose -s v -d 'Be more verbose (increase logging level)'
|
||||
complete -c btrbk -l quiet -s q -d 'Be quiet (do not print backup summary)'
|
||||
complete -c btrbk -l table -s t -d 'Change output to table format'
|
||||
complete -c btrbk -l long -s L -d 'Change output to long format'
|
||||
complete -c btrbk -l print-schedule -s S -d 'Print scheduler details (for the "run" command)'
|
||||
complete -c btrbk -l progress -d 'Show progress bar on send-receive operation'
|
||||
|
||||
# uncommon options from manpage
|
||||
complete -c btrbk -l single-column -s 1 -d 'Print output as a single column'
|
||||
complete -c btrbk -l pretty -d 'Print pretty table output with lowercase and underlined column headings'
|
||||
complete -c btrbk -l raw -d 'Create raw targets for archive command'
|
||||
|
||||
# subcommands
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a run -d 'Run snapshot and backup operations'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a dryrun -d 'Show what would be executed without running btrfs commands'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a snapshot -d 'Run snapshot operations only'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a resume -d 'Run backup operations and delete snapshots'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a prune -d 'Only delete snapshots and backups'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a archive -d 'Recursively copy all subvolumes (src -> dst)'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a clean -d 'Delete incomplete (garbled) backups'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a stats -d 'Print snapshot/backup statistics'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a usage -d 'Print filesystem usage'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a ls -d 'List all btrfs subvolumes below a given path'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a origin -d 'Print origin information for a subvolume'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a diff -d 'List file changes between related subvolumes'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a extents -d 'Calculate accurate disk space usage for a path'
|
||||
complete -c btrbk -f -n __fish_use_subcommand -a list -d 'List snapshots and backups'
|
||||
|
||||
# subsubcommands for "list"
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a all -d 'List all snapshots and backups'
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a snapshots -d 'List snapshots only'
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a backups -d 'List backups and correlated snapshots'
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a latest -d 'List most recent snapshots and backups'
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a config -d 'List configured source/snapshot/target relations'
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a source -d 'List configured source/snapshot relations'
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a volume -d 'List configured volume sections'
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from list' -f -a target -d 'List configured targets'
|
||||
|
||||
complete -c btrbk -n '__fish_seen_subcommand_from run dryrun snapshot resume prune clean' -x -a '(__fish_btrbk_complete_filter)'
|
||||
@@ -16,14 +16,12 @@ complete -c code -l user-data-dir -ra "(__fish_complete_directories)" -d 'Specif
|
||||
complete -c code -l profile -d 'Opens the provided folder or workspace with the given profile'
|
||||
complete -c code -s v -l version -d 'Print version'
|
||||
complete -c code -s h -l help -d 'Print usage'
|
||||
complete -c code -l folder-uri -d 'Opens a window with given folder uri(s)'
|
||||
complete -c code -l file-uri -d 'Opens a window with given file uri(s)'
|
||||
|
||||
# Extensions management
|
||||
complete -c code -l extensions-dir -d 'Set the root path for extensions'
|
||||
complete -c code -l extensions-dir -r -d 'Set the root path for extensions'
|
||||
complete -c code -l list-extensions -d 'List the installed extensions'
|
||||
complete -c code -l show-versions -d 'Show versions of installed extensions' -n '__fish_seen_argument -l list-extensions'
|
||||
complete -c code -l category -d 'Filters installed extensions by provided category' -n '__fish_seen_argument -l list-extensions'
|
||||
complete -c code -l category -x -d 'Filters installed extensions by provided category' -n '__fish_seen_argument -l list-extensions'
|
||||
complete -c code -l install-extension -ra "(__fish_complete_vscode_extensions)" -d 'Installs or updates the extension'
|
||||
complete -c code -l force -n '__fish_seen_argument -l install-extension' -d 'Updates to the latest version'
|
||||
complete -c code -l pre-release -n '__fish_seen_argument -l install-extension' -d 'Installs the pre-release version'
|
||||
@@ -35,12 +33,13 @@ complete -c code -l disable-extensions -d 'Disable all installed extensions'
|
||||
|
||||
# Troubleshooting
|
||||
complete -c code -l verbose -d 'Print verbose output (implies --wait)'
|
||||
complete -c code -l log -a 'critical error warn info debug trace off' -d 'Log level to use (default: info)'
|
||||
complete -c code -l log -xa 'critical error warn info debug trace off' -d 'Log level to use (default: info)'
|
||||
complete -c code -s s -l status -d 'Print process usage and diagnostics information'
|
||||
complete -c code -l prof-startup -d 'Run CPU profiler during startup'
|
||||
complete -c code -l sync -d 'Turn sync on or off'
|
||||
complete -c code -l inspect-extensions -d 'Allow debugging and profiling of extensions'
|
||||
complete -c code -l sync -xa 'on off' -d 'Turn sync on or off'
|
||||
complete -c code -l inspect-extensions -x -d 'Allow debugging and profiling of extensions'
|
||||
complete -c code -l inspect-brk-extensions -x -d 'Allow debugging and profiling of extensions'
|
||||
complete -c code -l disable-lcd-text -d 'Disable LCD font rendering'
|
||||
complete -c code -l disable-gpu -d 'Disable GPU hardware acceleration'
|
||||
complete -c code -l disable-chromium-sandbox -d 'Disable the Chromium sandbox environment'
|
||||
complete -c code -l telemetry -d 'Shows all telemetry events which VS code collects'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
function __dnf_list_installed_packages
|
||||
dnf repoquery --cacheonly "$cur*" --qf "%{name}" --installed </dev/null
|
||||
dnf repoquery --cacheonly "$cur*" --qf "%{name}\n" --installed </dev/null
|
||||
end
|
||||
|
||||
function __dnf_list_available_packages
|
||||
@@ -26,7 +26,7 @@ function __dnf_list_available_packages
|
||||
else
|
||||
# In some cases dnf will ask for input (e.g. to accept gpg keys).
|
||||
# Connect it to /dev/null to try to stop it.
|
||||
set results (dnf repoquery --cacheonly "$tok*" --qf "%{name}" --available </dev/null 2>/dev/null)
|
||||
set results (dnf repoquery --cacheonly "$tok*" --qf "%{name}\n" --available </dev/null 2>/dev/null)
|
||||
end
|
||||
if set -q results[1]
|
||||
set results (string match -r -- '.*\\.rpm$' $files) $results
|
||||
|
||||
@@ -1,26 +1 @@
|
||||
function __fish_exercism_no_subcommand -d 'Test if exercism has yet to be given the subcommand'
|
||||
for i in (commandline -xpc)
|
||||
if contains -- $i demo debug configure fetch restore submit unsubmit tracks download help
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
complete -c exercism -s c -l config -d 'path to config file [$EXERCISM_CONFIG_FILE, $XDG_CONFIG_HOME]'
|
||||
complete -c exercism -s v -l verbose -d "turn on verbose logging"
|
||||
complete -c exercism -s h -l help -d "show help"
|
||||
complete -c exercism -s v -l version -d "print the version"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a configure -d "Writes config values to a JSON file"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a debug -d "Outputs useful debug information"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a download -d "Downloads a solution given the ID of the latest iteration"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a fetch -d "Fetches the next unsubmitted problem in each track"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a list -d "Lists the available problems for a language track, given its ID"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a open -d "Opens exercism.io on given problem"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a restore -d "Downloads the most recent iteration for each of your solutions on exercism.io"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a skip -d "Skips a problem given a track ID and problem slug"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a status -d "Fetches information about your progress with a given language track"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a submit -d "Submits a new iteration to a problem on exercism.io"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a tracks -d "Lists the available language tracks"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a upgrade -d "Upgrades the CLI to the latest released version"
|
||||
complete -f -n __fish_exercism_no_subcommand -c exercism -a help -d "show help"
|
||||
exercism completion fish | source
|
||||
|
||||
2
share/completions/fish-lsp.fish
Normal file
2
share/completions/fish-lsp.fish
Normal file
@@ -0,0 +1,2 @@
|
||||
__fish_cache_sourced_completions fish-lsp complete
|
||||
or fish-lsp complete | source
|
||||
1
share/completions/folderify.fish
Normal file
1
share/completions/folderify.fish
Normal file
@@ -0,0 +1 @@
|
||||
folderify --completions fish | source
|
||||
1
share/completions/gcloud.fish
Normal file
1
share/completions/gcloud.fish
Normal file
@@ -0,0 +1 @@
|
||||
complete -c gcloud -f -a '(__fish_argcomplete_complete gcloud)'
|
||||
@@ -16,6 +16,7 @@ complete -c gem -n __fish_use_subcommand -xa cleanup -d "Cleanup old versions of
|
||||
complete -c gem -n __fish_use_subcommand -xa contents -d "Display the contents of the installed gems"
|
||||
complete -c gem -n __fish_use_subcommand -xa dependency -d "Show the dependencies of an installed gem"
|
||||
complete -c gem -n __fish_use_subcommand -xa environment -d "Display RubyGems environmental information"
|
||||
complete -c gem -n __fish_use_subcommand -xa fetch -d "Download a gem into current directory"
|
||||
complete -c gem -n __fish_use_subcommand -xa help -d "Provide help on the 'gem' command"
|
||||
complete -c gem -n __fish_use_subcommand -xa install -d "Install a gem into the local repository"
|
||||
complete -c gem -n __fish_use_subcommand -xa list -d "Display all gems whose name starts with STRING"
|
||||
@@ -86,6 +87,9 @@ complete $dep_opt -s p -l pipe -d "Pipe Format (name --version ver)"
|
||||
set -l env_opt -c gem -n 'contains environment (commandline -pxc)'
|
||||
complete $env_opt -xa "packageversion\t'display the package version' gemdir\t'display the path where gems are installed' gempath\t'display path used to search for gems' version\t'display the gem format version' remotesources\t'display the remote gem servers'"
|
||||
|
||||
set -l fetch_opt -c gem -n 'contains fetch (commandline -pxc)'
|
||||
complete $fetch_opt -s v -l version -d "Specify version of gem to download" -x
|
||||
|
||||
##
|
||||
# help
|
||||
set -l help_opt -c gem -n 'contains help (commandline -pxc)'
|
||||
|
||||
1
share/completions/gsutil.fish
Normal file
1
share/completions/gsutil.fish
Normal file
@@ -0,0 +1 @@
|
||||
complete -c gsutil -f -a '(__fish_argcomplete_complete gsutil)'
|
||||
@@ -15,7 +15,7 @@ complete -c htop -l readonly -d 'Disable all system and process changing feature
|
||||
complete -c htop -l version -s V -d 'Show version and exit'
|
||||
complete -c htop -l tree -s t -d 'Show processes in tree view'
|
||||
complete -c htop -l highlight-changes -s H -d 'Highlight new and old processes' -x
|
||||
complete -c htop -l drop-capabilites -d 'Drop unneeded Linux capabilites (Requires libpcap support)' -xka "
|
||||
complete -c htop -l drop-capabilities -d 'Drop unneeded Linux capabilities (Requires libpcap support)' -xka "
|
||||
off
|
||||
basic
|
||||
strict
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# Basic completions for simonw/llm
|
||||
# A complete implementation for `llm [prompt]` but other subcommands
|
||||
# can be further fleshed out.
|
||||
# Completions for simonw/llm
|
||||
|
||||
set -l subcmds prompt aliases chat collections embed embed-models embed-multi install keys logs models openai plugins similar templates uninstall
|
||||
function __fish_llm_subcmds
|
||||
@@ -10,7 +8,7 @@ function __fish_llm_subcmds
|
||||
"collections" "View/manage embedding collections" \
|
||||
"embed" "Embed text and get/store result" \
|
||||
"embed-models" "Manage available embedding models" \
|
||||
"embed-multi\Store embeddings for multiple strings" \
|
||||
"embed-multi" "Store embeddings for multiple strings" \
|
||||
"install" "Install PyPI packages into llm env" \
|
||||
"keys" "Manage stored API keys" \
|
||||
"logs" "Explore logged prompts/responses" \
|
||||
@@ -27,24 +25,117 @@ complete -c llm -n __fish_is_first_token -xa "(__fish_llm_subcmds)"
|
||||
# This applies to the base command only
|
||||
complete -c llm -n "not __fish_seen_subcommand_from $subcmds" -l version -d "Show version info"
|
||||
# This applies to the base command or any subcommands
|
||||
complete -c llm -l help -d "Show usage info"
|
||||
complete -c llm -l help -d "Show command usage info" -x
|
||||
|
||||
function __fish_llm_models
|
||||
llm models |
|
||||
string replace -r '^[^:]+: ([^ ]+)(?: \\(aliases: )?([^),]+,?)?+.*$' '$1 $2' |
|
||||
string replace -r '^[^:\\n]+: (\\S+)(?: \\(aliases: )?((?:[^),\\s]+,?)?+.*?)\\)?$' '$1 $2' |
|
||||
string split ' ' -n |
|
||||
string trim -c ','
|
||||
end
|
||||
|
||||
function __fish_embedding_models
|
||||
llm models |
|
||||
string replace -r '^[^:\\n]+: (\\S+)(?: \\(aliases: )?((?:[^),\\s]+,?)?+.*?)\\)?$' '$1 $2' |
|
||||
string split ' ' -n |
|
||||
string trim -c ','
|
||||
end
|
||||
|
||||
# The default subcommand is 'prompt'
|
||||
set -l is_prompt "not __fish_seen_subcommand_from $subcmds || __fish_seen_subcommand_from prompt"
|
||||
complete -c llm -n $is_prompt -s s -l system -d "System prompt to use"
|
||||
complete -c llm -n $is_prompt -s m -l model -d "Model to use" -xa "(__fish_llm_models)"
|
||||
complete -c llm -n $is_prompt -s a -l attachment -d "Attachment to use" -r -a'-'
|
||||
complete -c llm -n $is_prompt -l at -d "Attachment type" -r
|
||||
complete -c llm -n $is_prompt -l attachment-type -d "Attachment type" -r
|
||||
complete -c llm -n $is_prompt -s n -l no-log -d "Don't log to db"
|
||||
complete -c llm -n $is_prompt -s l -l log -d "Log prompt/reply to db"
|
||||
complete -c llm -n $is_prompt -s c -l continue -d "Continue most recent conversation"
|
||||
complete -c llm -n $is_prompt -l key -d "API key to use"
|
||||
complete -c llm -n $is_prompt -l save -d "Save prompt as template with name" -r
|
||||
set -l condition "not __fish_seen_subcommand_from $subcmds || __fish_seen_subcommand_from prompt"
|
||||
complete -c llm -n $condition -s s -l system -d "System prompt to use" -r
|
||||
complete -c llm -n $condition -s m -l model -d "Model to use" -xa "(__fish_llm_models)"
|
||||
complete -c llm -n $condition -s a -l attachment -d "Attachment to use" -ra'-'
|
||||
complete -c llm -n $condition -l at -d "Attachment type" -r
|
||||
complete -c llm -n $condition -l attachment-type -d "Attachment type" -r
|
||||
complete -c llm -n $condition -s n -l no-log -d "Don't log to db" -x
|
||||
complete -c llm -n $condition -s l -l log -d "Log prompt/reply to db" -x
|
||||
complete -c llm -n $condition -s c -l continue -d "Continue most recent conversation" -x
|
||||
complete -c llm -n $condition -l key -d "API key to use" -r
|
||||
complete -c llm -n $condition -l save -d "Save prompt as template with name" -x
|
||||
|
||||
# llm aliases
|
||||
set -l condition "__fish_seen_subcommand_from aliases"
|
||||
complete -c llm -n $condition -xa list -d "List current aliases" -x
|
||||
complete -c llm -n $condition -xa path -d "Print path of llm's aliases.json" -x
|
||||
complete -c llm -n $condition -xa remove -d "Remove an llm alias" -r
|
||||
complete -c llm -n $condition -xa set -d "Set an alias for a model" -r
|
||||
|
||||
# llm aliases
|
||||
set -l condition "__fish_seen_subcommand_from chat"
|
||||
complete -c llm -n $condition -s s -l system -d "System prompt to use" -r
|
||||
complete -c llm -n $condition -s m -l model -d "Model to use" -xa "(__fish_llm_models)"
|
||||
complete -c llm -n $condition -l cid -d "Continue conversation with given id" -x
|
||||
complete -c llm -n $condition -l conversation -d "Continue conversation with given id" -x
|
||||
complete -c llm -n $condition -s t -l template -d "Template to use" -x
|
||||
complete -c llm -n $condition -s p -l param -d "Set template parameter to value" -x
|
||||
complete -c llm -n $condition -s o -l option -d "Set key/value option for model" -x
|
||||
complete -c llm -n $condition -l no-stream -d "Do not stream output" -x
|
||||
complete -c llm -n $condition -l key -d "API key to use" -x
|
||||
|
||||
# llm collections
|
||||
set -l condition "__fish_seen_subcommand_from collections"
|
||||
complete -c llm -n $condition -xa list -d "List collections" -x
|
||||
complete -c llm -n $condition -xa delete -d "Delete specified collection" -x
|
||||
complete -c llm -n $condition -xa path -d "Print path to embeddings database" -x
|
||||
|
||||
# llm embed
|
||||
set -l condition "__fish_seen_subcommand_from embed"
|
||||
complete -c llm -n $condition -s i -l input -d "File to embed" -r
|
||||
complete -c llm -n $condition -s m -l model -d "Model to use" -xa "(__fish_embedding_models)"
|
||||
complete -c llm -n $condition -l store -d "Store the text itself in the db" -x
|
||||
complete -c llm -n $condition -s d -l database -d "Path to db to use" -r
|
||||
complete -c llm -n $condition -s c -l content -d "Text content to embed" -x
|
||||
complete -c llm -n $condition -l binary -d "Treat input as binary" -x
|
||||
complete -c llm -n $condition -l metadata -d "JSON object metadata to store" -x
|
||||
complete -c llm -n $condition -s f -l format -d "Output format" -xa "json blob base64 hex"
|
||||
|
||||
# llm embed-models
|
||||
set -l condition "__fish_seen_subcommand_from embed-models"
|
||||
complete -c llm -n $condition -xa list -d "List available embedding models" -x
|
||||
complete -c llm -n $condition -xa default -d "Show or set default embedding model" -x
|
||||
|
||||
# llm embed-multi
|
||||
set -l condition "__fish_seen_subcommand_from embed-multi"
|
||||
complete -c llm -n $condition -l format -xa "json csv tsv nl" -d "Format of input (default: auto-detected)"
|
||||
complete -c llm -n $condition -l files -r -d "Embed files in DIR matching GLOB"
|
||||
complete -c llm -n $condition -l encoding -r -d "Encoding to use when reading input"
|
||||
complete -c llm -n $condition -l binary -d "Treat input as binary"
|
||||
complete -c llm -n $condition -l sql -x -d "Read input using this SQL query"
|
||||
complete -c llm -n $condition -l attach -x -d "Attach db ALIAS from PATH"
|
||||
complete -c llm -n $condition -l batch-size -x -d "Batch size to use for embeddings"
|
||||
complete -c llm -n $condition -l prefix -x -d "Prefix to add to the IDs"
|
||||
complete -c llm -n $condition -s m -l model -d "Embedding model to use" -xa "(__fish_embedding_models)"
|
||||
complete -c llm -n $condition -l store -d "Store the text itself in the db"
|
||||
complete -c llm -n $condition -s d -l database -d "Path to db to use"
|
||||
|
||||
# llm install
|
||||
set -l condition "__fish_seen_subcommand_from install"
|
||||
complete -c llm -n $condition -s U -l upgrade -d "Upgrade packages to latest version" -x
|
||||
complete -c llm -n $condition -s e -l editable -d "Install project in editable mode from PATH" -r
|
||||
complete -c llm -n $condition -l force-reinstall -d "Reinstall all packages, even if up-to-date" -x
|
||||
complete -c llm -n $condition -l no-cache-dir -d "Disable cache" -x
|
||||
|
||||
# llm keys
|
||||
set -l condition "__fish_seen_subcommand_from keys"
|
||||
complete -c llm -n $condition -xa list -d "List names of all stored keys"
|
||||
complete -c llm -n $condition -xa get -d "Print saved key"
|
||||
complete -c llm -n $condition -xa path -d "Print path of llm's keys.json"
|
||||
complete -c llm -n $condition -xa set -d "Save a key in llm's keys.json"
|
||||
|
||||
# llm logs
|
||||
set -l condition "__fish_seen_subcommand_from logs"
|
||||
complete -c llm -n $condition -xa list -d "List recent prompts and responses"
|
||||
complete -c llm -n $condition -xa off -d "Turn off logging"
|
||||
complete -c llm -n $condition -xa on -d "Turn on logging"
|
||||
complete -c llm -n $condition -xa path -d "Print path to llm's logs.db"
|
||||
complete -c llm -n $condition -xa status -d "Show current status of db logging"
|
||||
|
||||
# llm models
|
||||
set -l condition "__fish_seen_subcommand_from models"
|
||||
complete -c llm -n $condition -xa list -d "List available models" -x
|
||||
complete -c llm -n $condition -xa default -d "Show or set default model" -x
|
||||
|
||||
# llm plugins
|
||||
set -l condition "__fish_seen_subcommand_from plugins"
|
||||
complete -c llm -n $condition -l all -d "Include built-in/default plugins" -x
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
complete -c md5sum -d "Compute and check message digest" -r
|
||||
complete -c md5sum -s b -l binary -d 'Read in binary mode'
|
||||
complete -c md5sum -s c -l check -d "Read sums from files and check them"
|
||||
complete -c md5sum -s t -l text -d 'Read in text mode'
|
||||
complete -c md5sum -l quiet -d 'Don''t print OK for each successfully verified file'
|
||||
complete -c md5sum -l status -d 'Don''t output anything, status code shows success'
|
||||
complete -c md5sum -s w -l warn -d 'Warn about improperly formatted checksum lines'
|
||||
complete -c md5sum -l tag -d 'Create a BSD-style checksum'
|
||||
complete -c md5sum -s t -l text -d 'Read in text mode (default)'
|
||||
complete -c md5sum -s z -l zero -d 'End each output line with NUL, not newline, and disable file name escaping'
|
||||
complete -c md5sum -l ignore-missing -d 'Don\'t fail or report status for missing files'
|
||||
complete -c md5sum -l quiet -d 'Don\'t print OK for each successfully verified file'
|
||||
complete -c md5sum -l status -d 'Don\'t output anything, status code shows success'
|
||||
complete -c md5sum -l strict -d 'With --check, exit non-zero for any invalid input'
|
||||
complete -c md5sum -s w -l warn -d 'Warn about improperly formatted checksum lines'
|
||||
complete -c md5sum -l help -d 'Display help text'
|
||||
complete -c md5sum -l version -d 'Output version information and exit'
|
||||
|
||||
@@ -141,7 +141,8 @@ complete -f -c npm -n '__fish_npm_using_command cache' -s h -l help -d 'Display
|
||||
# install-ci-test
|
||||
complete -f -c npm -n __fish_npm_needs_command -a 'ci clean-install' -d 'Clean install a project'
|
||||
complete -f -c npm -n __fish_npm_needs_command -a 'install-ci-test cit' -d 'Install a project with a clean slate and run tests'
|
||||
for c in ci clean-install ic install-clean install-clean install-ci-test cit clean-install-test sit
|
||||
# typos are intentional
|
||||
for c in ci clean-install ic install-clean isntall-clean install-ci-test cit clean-install-test sit
|
||||
complete -x -c npm -n "__fish_npm_using_command $c" -l install-strategy -a 'hoisted nested shallow linked' -d 'Install strategy'
|
||||
complete -x -c npm -n "__fish_npm_using_command $c" -l omit -a 'dev optional peer' -d 'Omit dependency type'
|
||||
complete -x -c npm -n "__fish_npm_using_command $c" -l strict-peer-deps -d 'Treat conflicting peerDependencies as failure'
|
||||
@@ -406,7 +407,8 @@ end
|
||||
complete -c npm -n __fish_npm_needs_command -a 'install add i' -d 'Install a package'
|
||||
complete -f -c npm -n __fish_npm_needs_command -a 'install-test it' -d 'Install package(s) and run tests'
|
||||
complete -f -c npm -n __fish_npm_needs_command -a 'link ln' -d 'Symlink a package folder'
|
||||
for c in install add i in ins inst insta instal isnt isnta isntal install install-test it link ln
|
||||
# typos are intentional
|
||||
for c in install add i in ins inst insta instal isnt isnta isntal isntall install-test it link ln
|
||||
complete -f -c npm -n "__fish_npm_using_command $c" -s S -l save -d 'Save to dependencies'
|
||||
complete -f -c npm -n "__fish_npm_using_command $c" -l no-save -d 'Prevents saving to dependencies'
|
||||
complete -f -c npm -n "__fish_npm_using_command $c" -s P -l save-prod -d 'Save to dependencies'
|
||||
@@ -726,4 +728,4 @@ complete -f -c npm -n '__fish_npm_using_command whoami' -a registry -d 'Check re
|
||||
complete -f -c npm -n '__fish_npm_using_command whoami' -s h -l help -d 'Display help'
|
||||
|
||||
# misc
|
||||
complete -f -c npm -n '__fish_seen_subcommand_from add i in ins inst insta instal isnt isnta isntal install; and not __fish_is_switch' -a "(__npm_filtered_list_packages \"$npm_install\")"
|
||||
complete -f -c npm -n '__fish_seen_subcommand_from add i in ins inst insta instal isnt isnta isntal isntall; and not __fish_is_switch' -a "(__npm_filtered_list_packages \"$npm_install\")"
|
||||
|
||||
@@ -1,23 +1,42 @@
|
||||
complete -c python -s B -d 'Don\'t write .py[co] files on import'
|
||||
complete -c python -s c -x -d "Execute argument as command"
|
||||
complete -c python -l check-hash-based-pycs -a "default always never" -d "Control validation behaviour of pyc files"
|
||||
complete -c python -s d -d "Debug on"
|
||||
complete -c python -s E -d "Ignore all PYTHON* env vars"
|
||||
complete -c python -s h -s '?' -l help -d "Display help and exit"
|
||||
complete -c python -s i -d "Interactive mode after executing commands"
|
||||
complete -c python -s m -d 'Run library module as a script (terminates option list)' -xa '(python -c "import pkgutil; print(\'\n\'.join([p[1] for p in pkgutil.iter_modules()]))")'
|
||||
complete -c python -s O -d "Enable optimizations"
|
||||
complete -c python -o OO -d "Remove doc-strings in addition to the -O optimizations"
|
||||
complete -c python -s s -d 'Don\'t add user site directory to sys.path'
|
||||
complete -c python -s S -d "Disable import of site module"
|
||||
complete -c python -s u -d "Unbuffered input and output"
|
||||
complete -c python -s v -d "Verbose mode"
|
||||
complete -c python -o vv -d "Even more verbose mode"
|
||||
complete -c python -s V -l version -d "Display version and exit"
|
||||
complete -c python -s W -x -d "Warning control" -a "ignore default all module once error"
|
||||
complete -c python -s x -d 'Skip first line of source, allowing use of non-Unix forms of #!cmd'
|
||||
complete -c python -f -n "__fish_is_nth_token 1" -k -a "(__fish_complete_suffix .py)"
|
||||
complete -c python -f -n "__fish_is_nth_token 1" -a - -d 'Read program from stdin'
|
||||
# This function adjusts for options with arguments (-X, -W, etc.), ensures completions stop when a script/module is set (-c, -m, file, -)
|
||||
function __fish_python_no_arg
|
||||
set -l num 1
|
||||
set -l tokens (commandline -pxc)
|
||||
set -l has_arg_list -X -W --check-hash-based-pycs
|
||||
if contains -- - $tokens
|
||||
set num (math $num - 1)
|
||||
end
|
||||
for has_arg in $has_arg_list
|
||||
if contains -- $has_arg $tokens
|
||||
set num (math $num + 1)
|
||||
end
|
||||
end
|
||||
if test (__fish_number_of_cmd_args_wo_opts) -gt $num
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
complete -c python -n __fish_python_no_arg -s B -d 'Don\'t write .py[co] files on import'
|
||||
complete -c python -n __fish_python_no_arg -s c -x -d "Execute argument as command"
|
||||
complete -c python -n __fish_python_no_arg -l check-hash-based-pycs -a "default always never" -d "Control validation behaviour of pyc files"
|
||||
complete -c python -n __fish_python_no_arg -s d -d "Debug on"
|
||||
complete -c python -n __fish_python_no_arg -s E -d "Ignore all PYTHON* env vars"
|
||||
complete -c python -n __fish_python_no_arg -s h -s '?' -l help -d "Display help and exit"
|
||||
complete -c python -n __fish_python_no_arg -s i -d "Interactive mode after executing commands"
|
||||
complete -c python -n __fish_python_no_arg -s m -d 'Run library module as a script (terminates option list)' -xa '(python -c "import pkgutil; print(\'\n\'.join([p[1] for p in pkgutil.iter_modules()]))")'
|
||||
complete -c python -n __fish_python_no_arg -s O -d "Enable optimizations"
|
||||
complete -c python -n __fish_python_no_arg -o OO -d "Remove doc-strings in addition to the -O optimizations"
|
||||
complete -c python -n __fish_python_no_arg -s s -d 'Don\'t add user site directory to sys.path'
|
||||
complete -c python -n __fish_python_no_arg -s S -d "Disable import of site module"
|
||||
complete -c python -n __fish_python_no_arg -s u -d "Unbuffered input and output"
|
||||
complete -c python -n __fish_python_no_arg -s v -d "Verbose mode"
|
||||
complete -c python -n __fish_python_no_arg -o vv -d "Even more verbose mode"
|
||||
complete -c python -n __fish_python_no_arg -s V -l version -d "Display version and exit"
|
||||
complete -c python -n __fish_python_no_arg -s W -x -d "Warning control" -a "ignore default all module once error"
|
||||
complete -c python -n __fish_python_no_arg -s x -d 'Skip first line of source, allowing use of non-Unix forms of #!cmd'
|
||||
complete -c python -n __fish_python_no_arg -f -k -a "(__fish_complete_suffix .py)"
|
||||
complete -c python -n __fish_python_no_arg -f -a - -d 'Read program from stdin'
|
||||
|
||||
# Version-specific completions
|
||||
# We have to detect this at runtime because pyenv etc can change
|
||||
@@ -25,10 +44,10 @@ complete -c python -f -n "__fish_is_nth_token 1" -a - -d 'Read program from stdi
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s2"' -s 3 -d 'Warn about Python 3.x incompatibilities that 2to3 cannot trivially fix'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s2"' -s t -d "Warn on mixed tabs and spaces"
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s2"' -s Q -x -a "old new warn warnall" -d "Division control"
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3"' -s q -d 'Don\'t print version and copyright messages on interactive startup'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3"' -s X -x -d 'Set implementation-specific option'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3"' -s b -d 'Warn when comparing bytes with str or int'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3"' -o bb -d 'Error when comparing bytes with str or int'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3"' -s R -d 'Turn on hash randomization'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3"' -s I -d 'Run in isolated mode'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3"' -o VV -d 'Print further version info'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3" && __fish_python_no_arg' -s q -d 'Don\'t print version and copyright messages on interactive startup'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3" && __fish_python_no_arg' -s X -x -d 'Set implementation-specific option'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3" && __fish_python_no_arg' -s b -d 'Warn when comparing bytes with str or int'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3" && __fish_python_no_arg' -o bb -d 'Error when comparing bytes with str or int'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3" && __fish_python_no_arg' -s R -d 'Turn on hash randomization'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3" && __fish_python_no_arg' -s I -d 'Run in isolated mode'
|
||||
complete -c python -n 'python -V 2>&1 | string match -rq "^.*\s3" && __fish_python_no_arg' -o VV -d 'Print further version info'
|
||||
|
||||
@@ -1,24 +1,43 @@
|
||||
complete -c python3 -s B -d 'Don\'t write .py[co] files on import'
|
||||
complete -c python3 -s c -x -d "Execute argument as command"
|
||||
complete -c python3 -s d -d "Debug on"
|
||||
complete -c python3 -s E -d "Ignore environment variables"
|
||||
complete -c python3 -s h -s '?' -l help -d "Display help and exit"
|
||||
complete -c python3 -s i -d "Interactive mode after executing commands"
|
||||
complete -c python3 -s O -d "Enable optimizations"
|
||||
complete -c python3 -o OO -d "Remove doc-strings in addition to the -O optimizations"
|
||||
complete -c python3 -s s -d 'Don\'t add user site directory to sys.path'
|
||||
complete -c python3 -s S -d "Disable import of site module"
|
||||
complete -c python3 -s u -d "Unbuffered input and output"
|
||||
complete -c python3 -s v -d "Verbose mode"
|
||||
complete -c python3 -s V -l version -d "Display version and exit"
|
||||
complete -c python3 -s W -x -d "Warning control" -a "ignore default all module once error"
|
||||
complete -c python3 -s x -d 'Skip first line of source, allowing use of non-Unix forms of #!cmd'
|
||||
complete -c python3 -n "__fish_is_nth_token 1" -k -fa "(__fish_complete_suffix .py)"
|
||||
complete -c python3 -f -n "__fish_is_nth_token 1" -a - -d 'Read program from stdin'
|
||||
complete -c python3 -s q -d 'Don\'t print version and copyright messages on interactive startup'
|
||||
complete -c python3 -s X -x -d 'Set implementation-specific option' -a 'faulthandler showrefcount tracemalloc showalloccount importtime dev utf8 pycache_prefex=PATH:'
|
||||
complete -c python3 -s b -d 'Issue warnings for possible misuse of `bytes` with `str`'
|
||||
complete -c python3 -o bb -d 'Issue errors for possible misuse of `bytes` with `str`'
|
||||
complete -c python3 -s m -d 'Run library module as a script (terminates option list)' -xa '(python3 -c "import pkgutil; print(\'\n\'.join([p[1] for p in pkgutil.iter_modules()]))")'
|
||||
complete -c python3 -l check-hash-based-pycs -d 'Set pyc hash check mode' -xa "default always never"
|
||||
complete -c python3 -s I -d 'Run in isolated mode'
|
||||
# This function adjusts for options with arguments (-X, -W, etc.), ensures completions stop when a script/module is set (-c, -m, file, -)
|
||||
function __fish_python_no_arg
|
||||
set -l num 1
|
||||
set -l tokens (commandline -pxc)
|
||||
set -l has_arg_list -X -W --check-hash-based-pycs
|
||||
if contains -- - $tokens
|
||||
set num (math $num - 1)
|
||||
end
|
||||
for has_arg in $has_arg_list
|
||||
if contains -- $has_arg $tokens
|
||||
set num (math $num + 1)
|
||||
end
|
||||
end
|
||||
if test (__fish_number_of_cmd_args_wo_opts) -gt $num
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
complete -c python3 -n __fish_python_no_arg -s B -d 'Don\'t write .py[co] files on import'
|
||||
complete -c python3 -n __fish_python_no_arg -s c -x -d "Execute argument as command"
|
||||
complete -c python3 -n __fish_python_no_arg -s d -d "Debug on"
|
||||
complete -c python3 -n __fish_python_no_arg -s E -d "Ignore environment variables"
|
||||
complete -c python3 -n __fish_python_no_arg -s h -s '?' -l help -d "Display help and exit"
|
||||
complete -c python3 -n __fish_python_no_arg -s i -d "Interactive mode after executing commands"
|
||||
complete -c python3 -n __fish_python_no_arg -s O -d "Enable optimizations"
|
||||
complete -c python3 -n __fish_python_no_arg -o OO -d "Remove doc-strings in addition to the -O optimizations"
|
||||
complete -c python3 -n __fish_python_no_arg -s s -d 'Don\'t add user site directory to sys.path'
|
||||
complete -c python3 -n __fish_python_no_arg -s S -d "Disable import of site module"
|
||||
complete -c python3 -n __fish_python_no_arg -s u -d "Unbuffered input and output"
|
||||
complete -c python3 -n __fish_python_no_arg -s v -d "Verbose mode"
|
||||
complete -c python3 -n __fish_python_no_arg -s V -l version -d "Display version and exit"
|
||||
complete -c python3 -n __fish_python_no_arg -s W -x -d "Warning control" -a "ignore default all module once error"
|
||||
complete -c python3 -n __fish_python_no_arg -s x -d 'Skip first line of source, allowing use of non-Unix forms of #!cmd'
|
||||
complete -c python3 -n __fish_python_no_arg -k -fa "(__fish_complete_suffix .py)"
|
||||
complete -c python3 -n __fish_python_no_arg -fa - -d 'Read program from stdin'
|
||||
complete -c python3 -n __fish_python_no_arg -s q -d 'Don\'t print version and copyright messages on interactive startup'
|
||||
complete -c python3 -n __fish_python_no_arg -s X -x -d 'Set implementation-specific option' -a 'faulthandler showrefcount tracemalloc showalloccount importtime dev utf8 pycache_prefex=PATH:'
|
||||
complete -c python3 -n __fish_python_no_arg -s b -d 'Issue warnings for possible misuse of `bytes` with `str`'
|
||||
complete -c python3 -n __fish_python_no_arg -o bb -d 'Issue errors for possible misuse of `bytes` with `str`'
|
||||
complete -c python3 -n __fish_python_no_arg -s m -d 'Run library module as a script (terminates option list)' -xa '(python3 -c "import pkgutil; print(\'\n\'.join([p[1] for p in pkgutil.iter_modules()]))")'
|
||||
complete -c python3 -n __fish_python_no_arg -l check-hash-based-pycs -d 'Set pyc hash check mode' -xa "default always never"
|
||||
complete -c python3 -n __fish_python_no_arg -s I -d 'Run in isolated mode'
|
||||
|
||||
@@ -18,6 +18,7 @@ end
|
||||
|
||||
function __resolvectl_commands
|
||||
printf "%b\n" "query\tResolve domain names or IP addresses" \
|
||||
"query\tResolve domain names, IPv4 and IPv6 addresses" \
|
||||
"service\tResolve service records" \
|
||||
"openpgp\tQuery PGP keys for email" \
|
||||
"tlsa\tQuery TLS public keys" \
|
||||
@@ -26,6 +27,9 @@ function __resolvectl_commands
|
||||
"reset-statistics\tReset statistics counters" \
|
||||
"flush-caches\tFlush DNS RR caches" \
|
||||
"reset-server-features\tFlushe all feature level information" \
|
||||
"monitor\tMonitor DNS queries" \
|
||||
"show-cache\tShow cache contents" \
|
||||
"show-server-state\tShow server state" \
|
||||
"dns\tSet per-interface DNS servers" \
|
||||
"domain\tSet per-interface search or routing domains" \
|
||||
"default-route\tSet per-interface default route flag" \
|
||||
|
||||
@@ -5,9 +5,9 @@ __fish_complete_ssh scp
|
||||
function __scp2ssh_port_number
|
||||
# There is a silly inconsistency between the ssh and scp commands regarding the short flag name
|
||||
# for specifying the TCP port number. This function deals with that by extracting the port
|
||||
# number if present and emitting it as a flag appropriate for ssh.
|
||||
# number if present.
|
||||
set -l port (commandline -c | string match -r -- ' -P ?(\d+)\b')
|
||||
and echo -p\n$port[2]
|
||||
and echo $port[2]
|
||||
end
|
||||
|
||||
function __scp_remote_target
|
||||
@@ -44,20 +44,25 @@ complete -c scp -d "Local Path" -n "not string match @ -- (commandline -ct)"
|
||||
|
||||
# Remote path
|
||||
# Get the list of remote files from the scp target.
|
||||
if string match -rq 'OpenSSH(_for_Windows)?_(?<major>\d+)\.*' -- (ssh -V 2>&1) && test "$major" -ge 9
|
||||
complete -c scp -d "Remote Path" -f -n "commandline -ct | string match -e ':'" -a "
|
||||
(__scp_remote_target):( \
|
||||
command ssh (__scp2ssh_port_number) -o 'BatchMode yes' (__scp_remote_target) command\ ls\ -dp\ (__scp_remote_path_prefix)\* 2>/dev/null
|
||||
)
|
||||
"
|
||||
else
|
||||
complete -c scp -d "Remote Path" -f -n "commandline -ct | string match -e ':'" -a "
|
||||
(__scp_remote_target):( \
|
||||
command ssh (__scp2ssh_port_number) -o 'BatchMode yes' (__scp_remote_target) command\ ls\ -dp\ (__scp_remote_path_prefix | string unescape)\* 2>/dev/null |
|
||||
complete -c scp -d "Remote Path" -f -n "commandline -ct | string match -e ':'" -a '
|
||||
(__scp_remote_target):(
|
||||
if not set -q __fish_scp_sftp
|
||||
set -l tmp (mktemp)
|
||||
if scp -P(__scp2ssh_port_number) -o "BatchMode yes" -q -O $tmp (__scp_remote_target):/dev/null
|
||||
set -g __fish_scp_sftp true
|
||||
else
|
||||
set -g __fish_scp_sftp false
|
||||
end
|
||||
rm $tmp
|
||||
end
|
||||
if $__fish_scp_sftp
|
||||
command ssh -p(__scp2ssh_port_number) -o "BatchMode yes" (__scp_remote_target) command\ ls\ -dp\ (__scp_remote_path_prefix)\* 2>/dev/null
|
||||
else
|
||||
command ssh -p(__scp2ssh_port_number) -o "BatchMode yes" (__scp_remote_target) command\ ls\ -dp\ (__scp_remote_path_prefix | string unescape)\* 2>/dev/null |
|
||||
string escape -n
|
||||
end
|
||||
)
|
||||
"
|
||||
end
|
||||
'
|
||||
|
||||
complete -c scp -s 3 -d "Copies between two remote hosts are transferred through the local host"
|
||||
complete -c scp -s B -d "Batch mode"
|
||||
|
||||
@@ -15,6 +15,7 @@ complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_com
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-full-job-control -d "Test if all new jobs are put under job control"
|
||||
|
||||
# The subcommands that are not "is-something" which don't change the fish state.
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a buildinfo -d "Print information on how this version fish was built"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-command -d "Print the name of the currently running command or function"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-commandline -d "Print the currently running command with its arguments"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-filename -d "Print the filename of the currently running script"
|
||||
|
||||
@@ -94,6 +94,8 @@ complete -c tmux -n __fish_use_subcommand -a $rename -d 'rename session'
|
||||
complete -c tmux -n __fish_use_subcommand -a $showmsgs -d 'save msgs in status bar in per-client msg log'
|
||||
|
||||
complete -c tmux -n __fish_use_subcommand -a $source -d 'execute commands from path'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $source" -F
|
||||
|
||||
complete -c tmux -n __fish_use_subcommand -a $start -d 'start tmux server if not running; do not create a session'
|
||||
|
||||
complete -c tmux -n __fish_use_subcommand -a $suspendc -d 'send SIGTSTP signal to client (tty stop)'
|
||||
@@ -113,14 +115,114 @@ complete -c tmux -xs t -n "__fish_seen_subcommand_from $detach $lockc $refresh $
|
||||
complete -c tmux -xs c -n "__fish_seen_subcommand_from $switchc" -a '(__fish_tmux_clients)' -d target-client
|
||||
|
||||
#commands with the -F format flag
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $lsc $ls" -rs F -d 'format string'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $lsc $ls" -xs F -d 'format string'
|
||||
|
||||
############### End: Clients and Sessions ###############
|
||||
|
||||
############### Begin: Windows and Panes ###############
|
||||
#TODO - these commands are not currently implemented.
|
||||
#there is a section in the tmux man page that has the same title as this section
|
||||
#use the "Clients and Sessions" code as an example when implementing this
|
||||
|
||||
set -l breakp "break-pane breakp"
|
||||
set -l capturep "capture-pane capturep"
|
||||
set -l chooseclient choose-client
|
||||
set -l choosetree choose-tree
|
||||
set -l customizemode customize-mode
|
||||
set -l displayp "display-panes displayp"
|
||||
set -l findw "find-window findw"
|
||||
set -l joinp "join-pane joinp move-pane movep"
|
||||
set -l killp "kill-pane killp"
|
||||
set -l killw "kill-window killw"
|
||||
set -l lastp "last-pane lastp"
|
||||
set -l lastw "last-window lastw"
|
||||
set -l linkw "link-window linkw"
|
||||
set -l lsp "list-panes lsp"
|
||||
set -l lsw "list-windows lsw"
|
||||
set -l movew "move-window movew"
|
||||
set -l neww "new-window neww"
|
||||
set -l nextl "next-layout nextl"
|
||||
set -l next "next-window next"
|
||||
set -l pipep "pipe-pane pipep"
|
||||
set -l prevl "previous-layout prevl"
|
||||
set -l prev "previous-window prev"
|
||||
set -l renamew "rename-window renamew"
|
||||
set -l resizep "resize-pane resizep"
|
||||
set -l resizew "resize-window resizew"
|
||||
set -l respawnp "respawn-pane respawnp"
|
||||
set -l respawnw "respawn-window respawnw"
|
||||
set -l rotatew "rotate-window rotatew"
|
||||
set -l selectl "select-layout selectl"
|
||||
set -l selectp "select-pane selectp"
|
||||
set -l selectw "select-window selectw"
|
||||
set -l splitw "split-window splitw"
|
||||
set -l swapp "swap-pane swapp"
|
||||
set -l swapw "swap-window swapw"
|
||||
set -l unlinkw "unlink-window unlinkw"
|
||||
|
||||
complete -c tmux -n __fish_use_subcommand -a $breakp -d 'break pane off into a new window'
|
||||
complete -c tmux -n __fish_use_subcommand -a $capturep -d 'capture contents of a pane into a buffer'
|
||||
complete -c tmux -n __fish_use_subcommand -a $chooseclient -d 'interactively choose client'
|
||||
complete -c tmux -n __fish_use_subcommand -a $choosetree -d 'interactively choose session/window/pane'
|
||||
complete -c tmux -n __fish_use_subcommand -a $customizemode -d 'interactively customize settings'
|
||||
complete -c tmux -n __fish_use_subcommand -a $displayp -d 'display a visible indicator for each pane'
|
||||
complete -c tmux -n __fish_use_subcommand -a $findw -d 'interactively choose window matching pattern'
|
||||
complete -c tmux -n __fish_use_subcommand -a $joinp -d 'split destination pane and move source pane into one of the halves'
|
||||
complete -c tmux -n __fish_use_subcommand -a $killp -d 'destroy a pane'
|
||||
complete -c tmux -n __fish_use_subcommand -a $killw -d 'destroy a window'
|
||||
complete -c tmux -n __fish_use_subcommand -a $lastp -d 'select the previusly selected pane'
|
||||
complete -c tmux -n __fish_use_subcommand -a $lastw -d 'select the previusly selected window'
|
||||
complete -c tmux -n __fish_use_subcommand -a $linkw -d 'link source window to destination window'
|
||||
complete -c tmux -n __fish_use_subcommand -a $lsp -d 'list panes'
|
||||
complete -c tmux -n __fish_use_subcommand -a $lsw -d 'list windows'
|
||||
complete -c tmux -n __fish_use_subcommand -a $movew -d 'move window'
|
||||
# TODO: Should accept shell command
|
||||
complete -c tmux -n __fish_use_subcommand -a $neww -d 'create a new window'
|
||||
complete -c tmux -n __fish_use_subcommand -a $nextl -d 'rearrange panes in a window according to the next layout'
|
||||
complete -c tmux -n __fish_use_subcommand -a $next -d 'move to the next window in the session'
|
||||
# TODO: Should accept shell command
|
||||
complete -c tmux -n __fish_use_subcommand -a $pipep -d 'pipe output from pane to a shell command'
|
||||
complete -c tmux -n __fish_use_subcommand -a $prevl -d 'rearrange panes in a window according to the previous layout'
|
||||
complete -c tmux -n __fish_use_subcommand -a $prev -d 'move to the previous window in the session'
|
||||
complete -c tmux -n __fish_use_subcommand -a $renamew -d 'rename a window'
|
||||
complete -c tmux -n __fish_use_subcommand -a $resizep -d 'resize a pane'
|
||||
complete -c tmux -n __fish_use_subcommand -a $resizew -d 'resize a window'
|
||||
# TODO: Should accept shell command
|
||||
complete -c tmux -n __fish_use_subcommand -a $respawnp -d 'reactivate a pane where a command exited'
|
||||
# TODO: Should accept shell command
|
||||
complete -c tmux -n __fish_use_subcommand -a $respawnw -d 'reactivate a window where a command exited'
|
||||
complete -c tmux -n __fish_use_subcommand -a $rotatew -d 'rotate panes within a window'
|
||||
|
||||
complete -c tmux -n __fish_use_subcommand -a $selectl -d 'rearrange panes according to a given layout'
|
||||
set -l layouts 'even-horizontal even-vertical main-horizontal main-horizontal-mirrored main-vertical main-vertical-mirrored tiled'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $selectl" -x -a "$layouts" -d 'predefined layout'
|
||||
|
||||
complete -c tmux -n __fish_use_subcommand -a $selectp -d 'activate specific pane'
|
||||
complete -c tmux -n __fish_use_subcommand -a $selectw -d 'activate specific window'
|
||||
# TODO: Should accept shell command
|
||||
complete -c tmux -n __fish_use_subcommand -a $splitw -d 'create a new pane by splitting target-pane'
|
||||
complete -c tmux -n __fish_use_subcommand -a $swapp -d 'swap two panes'
|
||||
complete -c tmux -n __fish_use_subcommand -a $swapw -d 'swap two windows'
|
||||
complete -c tmux -n __fish_use_subcommand -a $unlinkw -d 'unlink target-window'
|
||||
|
||||
## commands with pane flag
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $breakp $joinp $swapp" -xs s -a '(__fish_tmux_panes)' -d 'source pane'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $capturep $chooseclient $choosetree $customizemode $findw" -xs t -a '(__fish_tmux_panes)' -d 'target pane'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $killp $pipep $resizep $respawnp $selectl $selectp $splitw" -xs t -a '(__fish_tmux_panes)' -d 'target pane'
|
||||
# Unclear if there's a meaningful difference between "target pane" and "destination pane", but tmux makes the distinction
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $joinp $swapp" -xs t -a '(__fish_tmux_panes)' -d 'destination pane'
|
||||
|
||||
## commands with session flag
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $lastw $lsw $next $prev" -xs t -a '(__fish_tmux_sessions)' -d 'target session'
|
||||
|
||||
## commands with the -F format flag
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $breakp $lsp $lsw $neww $chooseclient $choosetree" -xs F -d 'format string'
|
||||
|
||||
## commands with -s/-t flags that are not panes/sessions (nice completion not yet implemented)
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $linkw $movew $swapw" -xs s -d 'source window'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $breakp $linkw $movew $neww $swapw" -xs t -d 'destination window'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $killw $lastp $nextl $prevl $renamew" -xs t -d 'target window'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $resizew $reswpawnw $rotatew $selectw $unlinkw" -xs t -d 'target window'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $displayp" -xs t -d 'target client'
|
||||
complete -c tmux -n "__fish_seen_subcommand_from $lsp" -xs t -d 'target'
|
||||
|
||||
############### End: Windows and Panes ###############
|
||||
|
||||
############### Begin: Key Bindings ###############
|
||||
|
||||
@@ -55,7 +55,7 @@ complete -c wget -o nd -d "Do not create a hierarchy of directories"
|
||||
complete -c wget -s x -l force-directories -d "Force creation of a hierarchy of directories"
|
||||
complete -c wget -l no-host-directories -d "Disable generation of host-prefixed directories"
|
||||
complete -c wget -o nH -d "Disable generation of host-prefixed directories"
|
||||
complete -c wget -l protocal-directories -d "Use the protocol name as a directory component"
|
||||
complete -c wget -l protocol-directories -d "Use the protocol name as a directory component"
|
||||
complete -c wget -l cut-dirs -d "Ignore specified number of directory components" -xa "1 2 3 4 5"
|
||||
complete -c wget -s P -l directory-prefix -d "Set directory prefix" -r
|
||||
complete -c wget -s E -l html-extension -d "Force html files to have html extension"
|
||||
|
||||
@@ -1,30 +1,4 @@
|
||||
function __fish_setup_cancel_text -v fish_color_cancel -v fish_color_normal
|
||||
set -g __fish_cancel_text "^C"
|
||||
if set -q fish_color_cancel
|
||||
set __fish_cancel_text (echo -sn (set_color $fish_color_cancel) $__fish_cancel_text (set_color normal))
|
||||
end
|
||||
if command -sq tput
|
||||
# Clear to EOL (to erase any autosuggestions)
|
||||
set __fish_cancel_text (echo -sn $__fish_cancel_text (tput el; or tput ce))
|
||||
end
|
||||
end
|
||||
__fish_setup_cancel_text
|
||||
|
||||
# This is meant to be bound to something like \cC.
|
||||
# This is meant to be bound to something like ctrl-c
|
||||
function __fish_cancel_commandline
|
||||
set -l cmd (commandline)
|
||||
if test -n "$cmd"
|
||||
echo -sn $__fish_cancel_text
|
||||
# `commandline -L` prints the line the cursor is on (starting from the prompt), so move the cursor
|
||||
# "to the end" then call `commandline -L` to get the total number of lines typed in at the prompt.
|
||||
commandline -C 10000000
|
||||
printf (string repeat -n (commandline -L) "\n")
|
||||
commandline ""
|
||||
emit fish_cancel
|
||||
end
|
||||
|
||||
# cancel: Close the pager if it's open (#4298)
|
||||
# repaint: Repaint even if we haven't cancelled anything so the prompt refreshes
|
||||
# and the terminal scrolls to it.
|
||||
commandline -f cancel -f repaint
|
||||
commandline -f cancel-commandline-traditional
|
||||
end
|
||||
|
||||
@@ -10,34 +10,31 @@ function __fish_complete_subcommand -d "Complete subcommand" --no-scope-shadowin
|
||||
case '--fcs-skip=*'
|
||||
set skip_next (string split = -- $arg)[2]
|
||||
case --commandline # --commandline means to use our arguments instead of the commandline.
|
||||
set subcommand $argv
|
||||
set -e argv
|
||||
break
|
||||
complete -C "$argv"
|
||||
return
|
||||
end
|
||||
end
|
||||
set -l options_with_param $argv
|
||||
|
||||
if not string length -q -- $subcommand
|
||||
set -l cmd (commandline -cxp | string escape) (commandline -ct)
|
||||
while set -q cmd[1]
|
||||
set -l token $cmd[1]
|
||||
set -e cmd[1]
|
||||
if contains -- $token $options_with_param
|
||||
set skip_next (math $skip_next + 1)
|
||||
set -l cmd (commandline -cxp | string escape) (commandline -ct)
|
||||
while set -q cmd[1]
|
||||
set -l token $cmd[1]
|
||||
set -e cmd[1]
|
||||
if contains -- $token $options_with_param
|
||||
set skip_next (math $skip_next + 1)
|
||||
continue
|
||||
end
|
||||
switch $token
|
||||
case '-*' '*=*'
|
||||
continue
|
||||
end
|
||||
switch $token
|
||||
case '-*' '*=*'
|
||||
case '*'
|
||||
if test $skip_next -gt 0
|
||||
set skip_next (math $skip_next - 1)
|
||||
continue
|
||||
case '*'
|
||||
if test $skip_next -gt 0
|
||||
set skip_next (math $skip_next - 1)
|
||||
continue
|
||||
end
|
||||
# found the start of our command
|
||||
set subcommand $token $cmd
|
||||
break
|
||||
end
|
||||
end
|
||||
# found the start of our command
|
||||
set subcommand $token $cmd
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -128,14 +128,6 @@ end" >$__fish_config_dir/config.fish
|
||||
complete -x -p "/etc/init.d/*" -a reload --description 'Reload service configuration'
|
||||
end
|
||||
|
||||
#
|
||||
# We want to show our completions for the [ (test) builtin, but
|
||||
# we don't want to create a [.fish. test.fish will not be loaded until
|
||||
# the user tries [ interactively.
|
||||
#
|
||||
complete -c [ --wraps test
|
||||
complete -c ! --wraps not
|
||||
|
||||
#
|
||||
# Only a few builtins take filenames; initialize the rest with no file completions
|
||||
#
|
||||
|
||||
@@ -66,9 +66,10 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod
|
||||
bind --preset $argv alt-l __fish_list_current_token
|
||||
bind --preset $argv alt-o __fish_preview_current_file
|
||||
bind --preset $argv alt-w __fish_whatis_current_token
|
||||
bind --preset $argv ctrl-l clear-screen
|
||||
bind --preset $argv ctrl-l scrollback-push repaint
|
||||
bind --preset $argv ctrl-c cancel-commandline
|
||||
bind --preset $argv ctrl-u backward-kill-line
|
||||
bind --preset $argv ctrl-k kill-line
|
||||
bind --preset $argv ctrl-w backward-kill-path-component
|
||||
bind --preset $argv end end-of-line
|
||||
bind --preset $argv home beginning-of-line
|
||||
|
||||
@@ -54,10 +54,7 @@ function edit_command_buffer --description 'Edit the command buffer in an extern
|
||||
end
|
||||
set cursor_from_editor (mktemp)
|
||||
set -a editor +$line "+norm! $col|" $f \
|
||||
'+autocmd VimLeave * ++once call writefile(
|
||||
[printf("%s %s %s", shellescape(bufname()), line("."), col("."))],
|
||||
"'$cursor_from_editor'"
|
||||
)'
|
||||
'+au VimLeave * ++once call writefile([printf("%s %s %s", shellescape(bufname()), line("."), col("."))], "'$cursor_from_editor'")'
|
||||
case emacs emacsclient gedit
|
||||
set -a editor +$line:$col $f
|
||||
case kak
|
||||
|
||||
19
share/functions/fish_jj_prompt.fish
Normal file
19
share/functions/fish_jj_prompt.fish
Normal file
@@ -0,0 +1,19 @@
|
||||
function fish_jj_prompt
|
||||
# If jj isn't installed, there's nothing we can do
|
||||
# Return 1 so the calling prompt can deal with it
|
||||
if not command -sq jj
|
||||
return 1
|
||||
end
|
||||
jj log 2>/dev/null --no-graph --ignore-working-copy --color=always --revisions @ \
|
||||
--template '
|
||||
concat(
|
||||
" ",
|
||||
separate(" ",
|
||||
format_short_change_id_with_hidden_and_divergent_info(self),
|
||||
bookmarks,
|
||||
tags,
|
||||
if(conflict, label("conflict", "×")),
|
||||
if(empty, label("empty", "(empty)"))
|
||||
),
|
||||
)'
|
||||
end
|
||||
@@ -1,7 +1,8 @@
|
||||
function fish_vcs_prompt --description "Print all vcs prompts"
|
||||
# If a prompt succeeded, we assume that it's printed the correct info.
|
||||
# This is so we don't try svn if git already worked.
|
||||
fish_git_prompt $argv
|
||||
fish_jj_prompt $argv
|
||||
or fish_git_prompt $argv
|
||||
or fish_hg_prompt $argv
|
||||
or fish_fossil_prompt $argv
|
||||
# The svn prompt is disabled by default because it's quite slow on common svn repositories.
|
||||
|
||||
@@ -312,6 +312,9 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
|
||||
bind -s --preset -M visual $key beginning-of-line
|
||||
end
|
||||
|
||||
bind -s --preset -M visual -m default v end-selection repaint-mode
|
||||
bind -s --preset -M visual -m insert i end-selection repaint-mode
|
||||
bind -s --preset -M visual -m insert I end-selection beginning-of-line repaint-mode
|
||||
bind -s --preset -M visual -m insert c kill-selection end-selection repaint-mode
|
||||
bind -s --preset -M visual -m insert s kill-selection end-selection repaint-mode
|
||||
bind -s --preset -M visual -m default d kill-selection end-selection backward-char repaint-mode
|
||||
@@ -321,6 +324,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
|
||||
bind -s --preset -M visual -m default '",*,y' "fish_clipboard_copy; commandline -f end-selection repaint-mode"
|
||||
bind -s --preset -M visual -m default '",+,y' "fish_clipboard_copy; commandline -f end-selection repaint-mode"
|
||||
bind -s --preset -M visual -m default '~' togglecase-selection end-selection repaint-mode
|
||||
bind -s --preset -M visual -m default g,U togglecase-selection end-selection repaint-mode
|
||||
|
||||
bind -s --preset -M visual -m default ctrl-c end-selection repaint-mode
|
||||
bind -s --preset -M visual -m default escape end-selection repaint-mode
|
||||
|
||||
@@ -34,6 +34,10 @@ function help --description 'Show help for the fish shell'
|
||||
if set -q BROWSER
|
||||
# User has manually set a preferred browser, so we respect that
|
||||
echo $BROWSER | read -at fish_browser
|
||||
if not type -q $fish_browser[1]
|
||||
printf (_ 'help: %s is not a valid command: %s\n') '$fish_browser' "$fish_browser"
|
||||
return 2
|
||||
end
|
||||
else
|
||||
# No browser set up, inferring.
|
||||
# We check a bunch and use the last we find.
|
||||
@@ -93,6 +97,11 @@ function help --description 'Show help for the fish shell'
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if not type -q $fish_browser[1]
|
||||
printf (_ 'help: %s is not a valid command: %s\n') '$fish_help_browser' "$fish_browser"
|
||||
return 2
|
||||
end
|
||||
end
|
||||
|
||||
# In Cygwin, start the user-specified browser using cygstart,
|
||||
|
||||
18
src/ast.rs
18
src/ast.rs
@@ -1255,7 +1255,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
token.typ == ParseTokenType::string
|
||||
&& !matches!(
|
||||
token.keyword,
|
||||
ParseKeyword::kw_end | ParseKeyword::kw_else | ParseKeyword::kw_case
|
||||
ParseKeyword::kw_case | ParseKeyword::kw_end | ParseKeyword::kw_else
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3337,6 +3337,14 @@ fn consume_excess_token_generating_error(&mut self) {
|
||||
ParseTokenType::string => {
|
||||
// There are three keywords which end a job list.
|
||||
match tok.keyword {
|
||||
ParseKeyword::kw_case => {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::unbalancing_case,
|
||||
"'case' builtin not inside of switch block"
|
||||
);
|
||||
}
|
||||
ParseKeyword::kw_end => {
|
||||
parse_error!(
|
||||
self,
|
||||
@@ -3353,14 +3361,6 @@ fn consume_excess_token_generating_error(&mut self) {
|
||||
"'else' builtin not inside of if block"
|
||||
);
|
||||
}
|
||||
ParseKeyword::kw_case => {
|
||||
parse_error!(
|
||||
self,
|
||||
tok,
|
||||
ParseErrorCode::unbalancing_case,
|
||||
"'case' builtin not inside of switch block"
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
internal_error!(
|
||||
self,
|
||||
|
||||
130
src/bin/fish.rs
130
src/bin/fish.rs
@@ -21,6 +21,8 @@
|
||||
#![allow(unstable_name_collisions)]
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
use fish::common::wcs2osstring;
|
||||
#[allow(unused_imports)]
|
||||
use fish::future::IsSomeAnd;
|
||||
use fish::{
|
||||
@@ -42,7 +44,7 @@
|
||||
fprintf, function, future_feature_flags as features,
|
||||
history::{self, start_private_mode},
|
||||
io::IoChain,
|
||||
nix::{getpid, isatty},
|
||||
nix::{getpid, getrusage, isatty, RUsage},
|
||||
panic::panic_handler,
|
||||
parse_constants::{ParseErrorList, ParseTreeFlags},
|
||||
parse_tree::ParsedSource,
|
||||
@@ -63,7 +65,6 @@
|
||||
};
|
||||
use std::ffi::{CString, OsStr, OsString};
|
||||
use std::fs::File;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
@@ -80,7 +81,7 @@
|
||||
#[cfg(feature = "installable")]
|
||||
// Disable for clippy because otherwise it would require sphinx
|
||||
#[cfg(not(clippy))]
|
||||
fn install(confirm: bool) -> bool {
|
||||
fn install(confirm: bool, dir: PathBuf) -> bool {
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
@@ -96,11 +97,6 @@ fn install(confirm: bool) -> bool {
|
||||
use std::io::ErrorKind;
|
||||
use std::io::Write;
|
||||
use std::io::{stderr, stdin};
|
||||
let Some(home) = fish::env::get_home() else {
|
||||
FLOG!(error, "Can't find home directory.");
|
||||
return false;
|
||||
};
|
||||
let dir = PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR);
|
||||
|
||||
// TODO: Translation,
|
||||
// FLOG?
|
||||
@@ -196,10 +192,9 @@ fn install(confirm: bool) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[cfg(any(clippy, not(feature = "installable")))]
|
||||
fn install(_confirm: bool) -> bool {
|
||||
eprintln!("Fish was built without support for self-installation");
|
||||
return false;
|
||||
#[cfg(clippy)]
|
||||
fn install(_confirm: bool, _dir: PathBuf) -> bool {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// container to hold the options specified within the command line
|
||||
@@ -241,13 +236,7 @@ fn tv_to_msec(tv: &libc::timeval) -> i64 {
|
||||
}
|
||||
|
||||
fn print_rusage_self() {
|
||||
let mut rs = MaybeUninit::uninit();
|
||||
if unsafe { libc::getrusage(libc::RUSAGE_SELF, rs.as_mut_ptr()) } != 0 {
|
||||
let s = CString::new("getrusage").unwrap();
|
||||
unsafe { libc::perror(s.as_ptr()) }
|
||||
return;
|
||||
}
|
||||
let rs: libc::rusage = unsafe { rs.assume_init() };
|
||||
let rs = getrusage(RUsage::RSelf);
|
||||
let rss_kb = if cfg!(target_os = "macos") {
|
||||
// mac use bytes.
|
||||
rs.ru_maxrss / 1024
|
||||
@@ -284,7 +273,7 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
|
||||
// Detect if we're running right out of the CMAKE build directory
|
||||
if exec_path.starts_with(env!("CARGO_MANIFEST_DIR")) {
|
||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
FLOG!(
|
||||
config,
|
||||
"Running out of target directory, using paths relative to CARGO_MANIFEST_DIR:\n",
|
||||
@@ -300,10 +289,17 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
}
|
||||
|
||||
if !done {
|
||||
// The next check is that we are in a reloctable directory tree
|
||||
// The next check is that we are in a relocatable directory tree
|
||||
if exec_path.ends_with("bin/fish") {
|
||||
let base_path = exec_path.parent().unwrap().parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
// One obvious path is ~/.local (with fish in ~/.local/bin/).
|
||||
// If we picked ~/.local/share/fish as our data path,
|
||||
// we would install there and erase history.
|
||||
// So let's isolate us a bit more.
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/fish/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share/fish"),
|
||||
sysconf: base_path.join("etc/fish"),
|
||||
doc: base_path.join("share/doc/fish"),
|
||||
@@ -316,6 +312,9 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
);
|
||||
let base_path = exec_path.parent().unwrap();
|
||||
paths = ConfigPaths {
|
||||
#[cfg(feature = "installable")]
|
||||
data: base_path.join("share/install"),
|
||||
#[cfg(not(feature = "installable"))]
|
||||
data: base_path.join("share"),
|
||||
sysconf: base_path.join("etc"),
|
||||
doc: base_path.join("user_doc/html"),
|
||||
@@ -339,14 +338,15 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
let Some(home) = fish::env::get_home() else {
|
||||
FLOG!(
|
||||
error,
|
||||
"Cannot find home directory and will refuse to read configuration"
|
||||
"Cannot find home directory and will refuse to read configuration.\n",
|
||||
"Consider installing into a directory tree with `fish --install=PATH`."
|
||||
);
|
||||
return paths;
|
||||
};
|
||||
|
||||
PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
} else {
|
||||
PathBuf::from(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
Path::new(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
};
|
||||
let bin = if cfg!(feature = "installable") {
|
||||
exec_path.parent().map(|x| x.to_path_buf())
|
||||
@@ -357,7 +357,7 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
||||
FLOG!(config, "Using compiled in paths:");
|
||||
paths = ConfigPaths {
|
||||
data,
|
||||
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
|
||||
sysconf: Path::new(SYSCONF_DIR).join("fish"),
|
||||
doc: DOC_DIR.into(),
|
||||
bin,
|
||||
}
|
||||
@@ -421,8 +421,7 @@ fn check_version_file(paths: &ConfigPaths, datapath: &wstr) -> Option<bool> {
|
||||
{
|
||||
// When fish is installable, we write the version to a file,
|
||||
// now we check it.
|
||||
let verfile =
|
||||
PathBuf::from(fish::common::wcs2osstring(datapath)).join("fish-install-version");
|
||||
let verfile = PathBuf::from(wcs2osstring(datapath)).join("fish-install-version");
|
||||
let version = std::fs::read_to_string(verfile).ok()?;
|
||||
|
||||
return Some(version == fish::BUILD_VERSION);
|
||||
@@ -458,7 +457,7 @@ fn read_init(parser: &Parser, paths: &ConfigPaths) {
|
||||
);
|
||||
}
|
||||
|
||||
install(true);
|
||||
install(true, PathBuf::from(wcs2osstring(&datapath)));
|
||||
// We try to go on if installation failed (or was rejected) here
|
||||
// If the assets are missing, we will trigger a later error,
|
||||
// if they are outdated, things will probably (tm) work somewhat.
|
||||
@@ -540,7 +539,7 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
||||
wopt(L!("no-config"), NoArgument, 'N'),
|
||||
wopt(L!("no-execute"), NoArgument, 'n'),
|
||||
wopt(L!("print-rusage-self"), NoArgument, RUSAGE_ARG),
|
||||
wopt(L!("install"), NoArgument, 'I'),
|
||||
wopt(L!("install"), OptionalArgument, 'I'),
|
||||
wopt(
|
||||
L!("print-debug-categories"),
|
||||
NoArgument,
|
||||
@@ -576,7 +575,60 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
||||
'h' => opts.batch_cmds.push("__fish_print_help fish".into()),
|
||||
'i' => opts.is_interactive_session = true,
|
||||
'I' => {
|
||||
install(false);
|
||||
#[cfg(not(feature = "installable"))]
|
||||
eprintln!("Fish was built without support for self-installation");
|
||||
#[cfg(feature = "installable")]
|
||||
if let Some(path) = w.woptarg {
|
||||
// We were given an explicit path.
|
||||
// Install us there as a relocatable install.
|
||||
// That means:
|
||||
// path/bin/fish is the fish binary
|
||||
// path/share/fish/ is the data directory
|
||||
// path/etc/fish is sysconf????
|
||||
use std::fs;
|
||||
let dir = PathBuf::from(wcs2osstring(path));
|
||||
if install(true, dir.join("share/fish/install")) {
|
||||
for sub in &["share/fish/install", "etc/fish", "bin"] {
|
||||
let p = dir.join(sub);
|
||||
let Ok(_) = fs::create_dir_all(p.clone()) else {
|
||||
eprintln!("Creating directory '{}' failed", p.display());
|
||||
std::process::exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
// Copy ourselves there.
|
||||
let argv0 = OsString::from_vec(wcs2string(&args[0]));
|
||||
let exec_path =
|
||||
get_executable_path(<OsString as AsRef<Path>>::as_ref(&argv0));
|
||||
let binpath = dir.join("bin/fish");
|
||||
if let Ok(exec_path) = exec_path.canonicalize() {
|
||||
if exec_path != binpath {
|
||||
if let Err(err) = std::fs::copy(exec_path, binpath.clone()) {
|
||||
FLOG!(error, "Cannot copy fish to", binpath.display());
|
||||
FLOG!(error, err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
println!(
|
||||
"Fish installed in '{}'. Start that from now on.",
|
||||
binpath.display()
|
||||
);
|
||||
// TODO: Reexec fish?
|
||||
std::process::exit(0);
|
||||
}
|
||||
} else {
|
||||
FLOG!(error, "Cannot copy fish to '%ls'. Please copy the fish binary there manually", binpath.display());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let paths = Some(determine_config_directory_paths(OsString::from_vec(
|
||||
wcs2string(&args[0]),
|
||||
)));
|
||||
let Some(paths) = paths else {
|
||||
FLOG!(error, "Cannot find config paths");
|
||||
std::process::exit(1);
|
||||
};
|
||||
install(true, paths.data);
|
||||
}
|
||||
}
|
||||
'l' => opts.is_login = true,
|
||||
'N' => {
|
||||
@@ -714,7 +766,7 @@ fn throwing_main() -> i32 {
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(debug_path.clone())
|
||||
.open(&debug_path)
|
||||
{
|
||||
Ok(dbg_file) => {
|
||||
// Rust sets O_CLOEXEC by default
|
||||
@@ -759,10 +811,9 @@ fn throwing_main() -> i32 {
|
||||
save_term_foreground_process_group();
|
||||
}
|
||||
|
||||
let mut paths: Option<ConfigPaths> = None;
|
||||
// If we're not executing, there's no need to find the config.
|
||||
if !opts.no_exec {
|
||||
paths = Some(determine_config_directory_paths(OsString::from_vec(
|
||||
let paths: Option<ConfigPaths> = if !opts.no_exec {
|
||||
let paths = Some(determine_config_directory_paths(OsString::from_vec(
|
||||
wcs2string(&args[0]),
|
||||
)));
|
||||
env_init(
|
||||
@@ -770,7 +821,10 @@ fn throwing_main() -> i32 {
|
||||
/* do uvars */ !opts.no_config,
|
||||
/* default paths */ opts.no_config,
|
||||
);
|
||||
}
|
||||
paths
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Set features early in case other initialization depends on them.
|
||||
// Start with the ones set in the environment, then those set on the command line (so the
|
||||
@@ -787,7 +841,7 @@ fn throwing_main() -> i32 {
|
||||
|
||||
// Construct the root parser!
|
||||
let env = Rc::new(EnvStack::globals().create_child(true /* dispatches_var_changes */));
|
||||
let parser: &Parser = &Parser::new(env, CancelBehavior::Clear);
|
||||
let parser = &Parser::new(env, CancelBehavior::Clear);
|
||||
parser.set_syncs_uvars(!opts.no_config);
|
||||
|
||||
if !opts.no_exec && !opts.no_config {
|
||||
@@ -950,7 +1004,6 @@ fn fish_xdm_login_hack_hack_hack_hack(cmds: &mut [OsString], args: &[WString]) -
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut result = false;
|
||||
let cmd = &cmds[0];
|
||||
if cmd == "exec \"${@}\"" || cmd == "exec \"$@\"" {
|
||||
// We're going to construct a new command that starts with exec, and then has the
|
||||
@@ -962,7 +1015,8 @@ fn fish_xdm_login_hack_hack_hack_hack(cmds: &mut [OsString], args: &[WString]) -
|
||||
}
|
||||
|
||||
cmds[0] = new_cmd;
|
||||
result = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
|
||||
use std::{ops::ControlFlow, os::unix::prelude::OsStrExt};
|
||||
|
||||
use libc::{STDIN_FILENO, TCSANOW, VEOF, VINTR};
|
||||
use libc::{STDIN_FILENO, STDOUT_FILENO, TCSANOW, VEOF, VINTR};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use fish::future::IsSomeAnd;
|
||||
use fish::{
|
||||
builtins::shared::BUILTIN_ERR_UNKNOWN,
|
||||
common::{shell_modes, str2wcstring, PROGRAM_NAME},
|
||||
common::{shell_modes, str2wcstring, write_loop, PROGRAM_NAME},
|
||||
env::env_init,
|
||||
eprintf, fprintf,
|
||||
input::input_terminfo_get_name,
|
||||
input_common::{
|
||||
terminal_protocol_hacks, terminal_protocols_enable_ifn, CharEvent, InputEventQueue,
|
||||
InputEventQueuer,
|
||||
InputEventQueuer, KITTY_PROGRESSIVE_ENHANCEMENTS_QUERY,
|
||||
},
|
||||
key::{self, char_to_symbol, Key},
|
||||
key::{char_to_symbol, Key},
|
||||
panic::panic_handler,
|
||||
print_help::print_help,
|
||||
printf,
|
||||
@@ -101,9 +101,6 @@ fn process_input(continuous_mode: bool, verbose: bool) -> i32 {
|
||||
continue;
|
||||
};
|
||||
let c = kevt.key.codepoint;
|
||||
if c == key::Invalid {
|
||||
continue;
|
||||
}
|
||||
if verbose {
|
||||
printf!("# decoded from: ");
|
||||
for byte in kevt.seq.chars() {
|
||||
@@ -140,6 +137,7 @@ fn setup_and_process_keys(continuous_mode: bool, verbose: bool) -> i32 {
|
||||
unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, &*shell_modes()) };
|
||||
|
||||
terminal_protocol_hacks();
|
||||
let _ = write_loop(&STDOUT_FILENO, KITTY_PROGRESSIVE_ENHANCEMENTS_QUERY);
|
||||
|
||||
if continuous_mode {
|
||||
eprintf!("\n");
|
||||
|
||||
@@ -686,15 +686,15 @@ pub fn read(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
|
||||
continue;
|
||||
}
|
||||
|
||||
// todo!("don't clone")
|
||||
let delimiter = opts
|
||||
.delimiter
|
||||
.clone()
|
||||
.or_else(|| {
|
||||
let ifs = parser.vars().get_unless_empty(L!("IFS"));
|
||||
ifs.map(|ifs| ifs.as_string())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let mut ifs_delimiter = WString::new();
|
||||
let delimiter: &wstr = opts.delimiter.as_deref().unwrap_or_else(|| {
|
||||
ifs_delimiter = parser
|
||||
.vars()
|
||||
.get_unless_empty(L!("IFS"))
|
||||
.map(|var| var.as_string())
|
||||
.unwrap_or_default();
|
||||
&ifs_delimiter
|
||||
});
|
||||
|
||||
if delimiter.is_empty() {
|
||||
// Every character is a separate token with one wrinkle involving non-array mode where
|
||||
@@ -735,7 +735,7 @@ pub fn read(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
|
||||
if opts.delimiter.is_none() {
|
||||
// We're using IFS, so tokenize the buffer using each IFS char. This is for backward
|
||||
// compatibility with old versions of fish.
|
||||
let tokens = split_string_tok(&buff, &delimiter, None)
|
||||
let tokens = split_string_tok(&buff, delimiter, None)
|
||||
.into_iter()
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
@@ -743,7 +743,7 @@ pub fn read(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
|
||||
var_ptr += 1;
|
||||
} else {
|
||||
// We're using a delimiter provided by the user so use the `string split` behavior.
|
||||
let splits = split_about(&buff, &delimiter, usize::MAX, false)
|
||||
let splits = split_about(&buff, delimiter, usize::MAX, false)
|
||||
.into_iter()
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
@@ -757,7 +757,7 @@ pub fn read(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
|
||||
// compatibility with old versions of fish.
|
||||
// Note the final variable gets any remaining text.
|
||||
let mut var_vals: Vec<WString> =
|
||||
split_string_tok(&buff, &delimiter, Some(vars_left(var_ptr)))
|
||||
split_string_tok(&buff, delimiter, Some(vars_left(var_ptr)))
|
||||
.into_iter()
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
@@ -775,7 +775,7 @@ pub fn read(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
|
||||
// We're using a delimiter provided by the user so use the `string split` behavior.
|
||||
// We're making at most argc - 1 splits so the last variable
|
||||
// is set to the remaining string.
|
||||
let splits = split_about(&buff, &delimiter, argc - 1, false);
|
||||
let splits = split_about(&buff, delimiter, argc - 1, false);
|
||||
assert!(splits.len() <= vars_left(var_ptr));
|
||||
for split in splits {
|
||||
parser.set_var_and_fire(argv[var_ptr], opts.place, vec![split.to_owned()]);
|
||||
|
||||
@@ -50,7 +50,7 @@ pub fn r#return(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
|
||||
Err(e) => return e,
|
||||
};
|
||||
|
||||
let has_function_block = parser.blocks().iter().any(|b| b.is_function_call());
|
||||
let has_function_block = parser.blocks_iter_rev().any(|b| b.is_function_call());
|
||||
|
||||
// *nix does not support negative return values, but our `return` builtin happily accepts being
|
||||
// called with negative literals (e.g. `return -1`).
|
||||
|
||||
@@ -873,7 +873,7 @@ fn builtin_break_continue(
|
||||
// Paranoia: ensure we have a real loop.
|
||||
// This is checked in the AST but we may be invoked dynamically, e.g. just via "eval break".
|
||||
let mut has_loop = false;
|
||||
for b in parser.blocks().iter().rev() {
|
||||
for b in parser.blocks_iter_rev() {
|
||||
if [BlockType::while_block, BlockType::for_block].contains(&b.typ()) {
|
||||
has_loop = true;
|
||||
break;
|
||||
|
||||
@@ -57,12 +57,14 @@ enum StatusCmd {
|
||||
STATUS_STACK_TRACE,
|
||||
STATUS_TEST_FEATURE,
|
||||
STATUS_CURRENT_COMMANDLINE,
|
||||
STATUS_BUILDINFO,
|
||||
}
|
||||
|
||||
str_enum!(
|
||||
StatusCmd,
|
||||
(STATUS_BASENAME, "basename"),
|
||||
(STATUS_BASENAME, "current-basename"),
|
||||
(STATUS_BUILDINFO, "buildinfo"),
|
||||
(STATUS_CURRENT_CMD, "current-command"),
|
||||
(STATUS_CURRENT_COMMANDLINE, "current-commandline"),
|
||||
(STATUS_DIRNAME, "current-dirname"),
|
||||
@@ -432,6 +434,45 @@ pub fn status(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
match s {
|
||||
STATUS_BUILDINFO => {
|
||||
let version = str2wcstring(crate::BUILD_VERSION.as_bytes());
|
||||
let target = str2wcstring(env!("BUILD_TARGET_TRIPLE").as_bytes());
|
||||
let host = str2wcstring(env!("BUILD_HOST_TRIPLE").as_bytes());
|
||||
let profile = str2wcstring(env!("BUILD_PROFILE").as_bytes());
|
||||
streams.out.append(L!("Build system: "));
|
||||
let buildsystem = match option_env!("CMAKE") {
|
||||
Some("1") => "CMake",
|
||||
_ => "Cargo",
|
||||
};
|
||||
streams.out.appendln(str2wcstring(buildsystem.as_bytes()));
|
||||
streams.out.append(L!("Version: "));
|
||||
streams.out.appendln(version);
|
||||
if target == host {
|
||||
streams.out.append(L!("Target (and host): "));
|
||||
streams.out.appendln(target);
|
||||
} else {
|
||||
streams.out.append(L!("Target: "));
|
||||
streams.out.appendln(target);
|
||||
streams.out.append(L!("Host: "));
|
||||
streams.out.appendln(host);
|
||||
}
|
||||
streams.out.append(L!("Profile: "));
|
||||
streams.out.appendln(profile);
|
||||
streams.out.append(L!("Features: "));
|
||||
let features: &[&str] = &[
|
||||
#[cfg(gettext)]
|
||||
"gettext",
|
||||
#[cfg(feature = "installable")]
|
||||
"installable",
|
||||
#[cfg(target_feature = "crt-static")]
|
||||
"crt-static",
|
||||
];
|
||||
streams
|
||||
.out
|
||||
.appendln(str2wcstring(features.join(" ").as_bytes()));
|
||||
streams.out.appendln("");
|
||||
return STATUS_CMD_OK;
|
||||
}
|
||||
STATUS_BASENAME | STATUS_DIRNAME | STATUS_FILENAME => {
|
||||
let res = parser.current_filename();
|
||||
let function = res.unwrap_or_default();
|
||||
|
||||
@@ -852,10 +852,10 @@ fn parse_number(arg: &wstr, number: &mut Number, errors: &mut Vec<WString>) -> b
|
||||
} else {
|
||||
errors.push(wgettext_fmt!("Argument is not a number: '%ls'", arg));
|
||||
}
|
||||
} else if floating.map_or(false, |x| x.is_nan()) {
|
||||
} else if floating.is_ok_and(|x| x.is_nan()) {
|
||||
// NaN is an error as far as we're concerned.
|
||||
errors.push(wgettext!("Not a number").to_owned());
|
||||
} else if floating.map_or(false, |x| x.is_infinite()) {
|
||||
} else if floating.is_ok_and(|x| x.is_infinite()) {
|
||||
errors.push(wgettext!("Number is infinite").to_owned());
|
||||
} else if integral == Err(Error::Overflow) {
|
||||
errors.push(wgettext_fmt!("Result too large: %ls", arg));
|
||||
|
||||
@@ -36,13 +36,22 @@ fn find_wait_handles(
|
||||
handles: &mut Vec<WaitHandleRef>,
|
||||
) -> bool {
|
||||
// Has a job already completed?
|
||||
// TODO: we can avoid traversing this list if searching by pid.
|
||||
let mut matched = false;
|
||||
let wait_handles: &mut WaitHandleStore = &mut parser.mut_wait_handles();
|
||||
for wh in wait_handles.iter() {
|
||||
if wait_handle_matches(query, wh) {
|
||||
handles.push(wh.clone());
|
||||
matched = true;
|
||||
match query {
|
||||
WaitHandleQuery::Pid(pid) => {
|
||||
if let Some(wh) = wait_handles.get_by_pid(pid) {
|
||||
handles.push(wh);
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
for wh in wait_handles.iter() {
|
||||
if wait_handle_matches(query, wh) {
|
||||
handles.push(wh.clone());
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1369,9 +1369,7 @@ pub fn valid_func_name(name: &wstr) -> bool {
|
||||
|
||||
/// A rusty port of the C++ `write_loop()` function from `common.cpp`. This should be deprecated in
|
||||
/// favor of native rust read/write methods at some point.
|
||||
///
|
||||
/// Returns the number of bytes written or an IO error.
|
||||
pub fn write_loop<Fd: AsRawFd>(fd: &Fd, buf: &[u8]) -> std::io::Result<usize> {
|
||||
pub fn write_loop<Fd: AsRawFd>(fd: &Fd, buf: &[u8]) -> std::io::Result<()> {
|
||||
let fd = fd.as_raw_fd();
|
||||
let mut total = 0;
|
||||
while total < buf.len() {
|
||||
@@ -1387,7 +1385,7 @@ pub fn write_loop<Fd: AsRawFd>(fd: &Fd, buf: &[u8]) -> std::io::Result<usize> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(total)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A rusty port of the C++ `read_loop()` function from `common.cpp`. This should be deprecated in
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::ops::Range;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::future::IsSomeAnd;
|
||||
use crate::highlight::HighlightSpec;
|
||||
@@ -142,31 +144,8 @@ pub fn at(&self, idx: usize) -> char {
|
||||
self.text.char_at(idx)
|
||||
}
|
||||
|
||||
pub fn line_at_cursor(&self) -> &wstr {
|
||||
let start = self.text[0..self.position()]
|
||||
.as_char_slice()
|
||||
.iter()
|
||||
.rposition(|&c| c == '\n')
|
||||
.map(|newline| newline + 1)
|
||||
.unwrap_or(0);
|
||||
let end = self.text[self.position()..]
|
||||
.as_char_slice()
|
||||
.iter()
|
||||
.position(|&c| c == '\n')
|
||||
.map(|pos| self.position() + pos)
|
||||
.unwrap_or(self.len());
|
||||
// Remove any traililng newline
|
||||
self.text[start..end].trim_matches('\n')
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
if self.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.push_edit(
|
||||
Edit::new(0..self.len(), L!("").to_owned()),
|
||||
/*allow_coalesce=*/ false,
|
||||
);
|
||||
pub fn offset_to_line(&self, offset: usize) -> usize {
|
||||
self.text[0..offset].chars().filter(|&c| c == '\n').count()
|
||||
}
|
||||
|
||||
/// Modify the commandline according to @edit. Most modifications to the
|
||||
@@ -186,15 +165,15 @@ pub fn push_edit(&mut self, mut edit: Edit, allow_coalesce: bool) {
|
||||
return;
|
||||
}
|
||||
|
||||
if range.is_empty() && edit.replacement.is_empty() {
|
||||
return; // nop
|
||||
}
|
||||
|
||||
// Assign a new group id or propagate the old one if we're in a logical grouping of edits
|
||||
if self.edit_group_level.is_some() {
|
||||
edit.group_id = Some(self.edit_group_id);
|
||||
}
|
||||
|
||||
let edit_does_nothing = range.is_empty() && edit.replacement.is_empty();
|
||||
if edit_does_nothing {
|
||||
return;
|
||||
}
|
||||
if self.undo_history.edits_applied != self.undo_history.edits.len() {
|
||||
// After undoing some edits, the user is making a new edit;
|
||||
// we are about to create a new edit branch.
|
||||
@@ -362,3 +341,27 @@ fn cursor_position_after_edit(edit: &Edit) -> usize {
|
||||
let removed = chars_deleted_left_of_cursor(edit);
|
||||
cursor.saturating_sub(removed)
|
||||
}
|
||||
|
||||
pub fn range_of_line_at_cursor(buffer: &wstr, cursor: usize) -> Range<usize> {
|
||||
let start = buffer[0..cursor]
|
||||
.as_char_slice()
|
||||
.iter()
|
||||
.rposition(|&c| c == '\n')
|
||||
.map(|newline| newline + 1)
|
||||
.unwrap_or(0);
|
||||
let mut end = buffer[cursor..]
|
||||
.as_char_slice()
|
||||
.iter()
|
||||
.position(|&c| c == '\n')
|
||||
.map(|pos| cursor + pos)
|
||||
.unwrap_or(buffer.len());
|
||||
// Remove any trailing newline
|
||||
if end != start && buffer.char_at(end - 1) == '\n' {
|
||||
end -= 1;
|
||||
}
|
||||
start..end
|
||||
}
|
||||
|
||||
pub fn line_at_cursor(buffer: &wstr, cursor: usize) -> &wstr {
|
||||
&buffer[range_of_line_at_cursor(buffer, cursor)]
|
||||
}
|
||||
|
||||
4
src/env/environment.rs
vendored
4
src/env/environment.rs
vendored
@@ -27,7 +27,7 @@
|
||||
use crate::wutil::{fish_wcstol, wgetcwd, wgettext};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use libc::{c_int, uid_t, STDOUT_FILENO, _IONBF};
|
||||
use libc::{c_int, confstr, uid_t, STDOUT_FILENO, _IONBF};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
@@ -568,7 +568,7 @@ fn setup_user(vars: &EnvStack) {
|
||||
|
||||
/// Make sure the PATH variable contains something.
|
||||
fn setup_path() {
|
||||
use crate::libc::{confstr, _CS_PATH};
|
||||
use crate::libc::_CS_PATH;
|
||||
|
||||
let vars = EnvStack::globals();
|
||||
let path = vars.get_unless_empty(L!("PATH"));
|
||||
|
||||
32
src/env/environment_impl.rs
vendored
32
src/env/environment_impl.rs
vendored
@@ -14,7 +14,7 @@
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wutil::fish_wcstol_radix;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CString;
|
||||
@@ -201,16 +201,28 @@ fn changed_exported(&mut self) {
|
||||
}
|
||||
}
|
||||
|
||||
// RefCell except we promise it can be used as Sync.
|
||||
// Safety: in order to do anything with this, the caller must be holding ENV_LOCK.
|
||||
struct EnvNodeSyncCell(RefCell<EnvNode>);
|
||||
|
||||
impl EnvNodeSyncCell {
|
||||
fn new(node: EnvNode) -> Self {
|
||||
Self(RefCell::new(node))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for EnvNodeSyncCell {}
|
||||
|
||||
/// EnvNodeRef is a reference to an EnvNode. It may be shared between different environments.
|
||||
/// The type Arc<RefCell<...>> may look suspicious, but all accesses to the EnvNode are protected by a global lock.
|
||||
/// All accesses to the EnvNode are protected by a global lock.
|
||||
#[derive(Clone)]
|
||||
struct EnvNodeRef(Arc<RefCell<EnvNode>>);
|
||||
struct EnvNodeRef(Arc<EnvNodeSyncCell>);
|
||||
|
||||
impl Deref for EnvNodeRef {
|
||||
type Target = RefCell<EnvNode>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
&self.0 .0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +231,7 @@ fn new(is_new_scope: bool, next: Option<EnvNodeRef>) -> EnvNodeRef {
|
||||
// Accesses are protected by the global lock.
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
EnvNodeRef(Arc::new(RefCell::new(EnvNode {
|
||||
EnvNodeRef(Arc::new(EnvNodeSyncCell::new(EnvNode {
|
||||
env: VarTable::new(),
|
||||
new_scope: is_new_scope,
|
||||
export_gen: 0,
|
||||
@@ -248,9 +260,6 @@ fn iter(&self) -> EnvNodeIter {
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: in order to do anything with an EnvNodeRef, the caller must be holding ENV_LOCK.
|
||||
unsafe impl Sync for EnvNodeRef {}
|
||||
|
||||
/// Helper to iterate over a chain of EnvNodeRefs.
|
||||
struct EnvNodeIter {
|
||||
current: Option<EnvNodeRef>,
|
||||
@@ -276,10 +285,7 @@ fn next(&mut self) -> Option<EnvNodeRef> {
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// All accesses to the EnvNode are protected by a global lock.
|
||||
static ref GLOBAL_NODE: EnvNodeRef = EnvNodeRef::new(false, None);
|
||||
}
|
||||
static GLOBAL_NODE: Lazy<EnvNodeRef> = Lazy::new(|| EnvNodeRef::new(false, None));
|
||||
|
||||
/// Recursive helper to snapshot a series of nodes.
|
||||
fn copy_node_chain(node: &EnvNodeRef) -> EnvNodeRef {
|
||||
@@ -293,7 +299,7 @@ fn copy_node_chain(node: &EnvNodeRef) -> EnvNodeRef {
|
||||
};
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
EnvNodeRef(Arc::new(RefCell::new(new_node)))
|
||||
EnvNodeRef(Arc::new(EnvNodeSyncCell::new(new_node)))
|
||||
}
|
||||
|
||||
/// A struct wrapping up parser-local variables. These are conceptually variables that differ in
|
||||
|
||||
16
src/env/var.rs
vendored
16
src/env/var.rs
vendored
@@ -102,7 +102,7 @@ pub struct EnvVarFlags: u8 {
|
||||
pub struct EnvVar {
|
||||
/// The list of values in this variable.
|
||||
/// Arc allows for cheap copying
|
||||
values: Arc<Box<[WString]>>,
|
||||
values: Arc<[WString]>,
|
||||
/// The variable's flags.
|
||||
flags: EnvVarFlags,
|
||||
}
|
||||
@@ -111,8 +111,8 @@ impl Default for EnvVar {
|
||||
fn default() -> Self {
|
||||
use std::sync::OnceLock;
|
||||
/// A shared read-only empty list.
|
||||
static EMPTY_LIST: OnceLock<Arc<Box<[WString]>>> = OnceLock::new();
|
||||
let empty_list = EMPTY_LIST.get_or_init(|| Arc::new(Box::new([])));
|
||||
static EMPTY_LIST: OnceLock<Arc<[WString]>> = OnceLock::new();
|
||||
let empty_list = EMPTY_LIST.get_or_init(|| Arc::new([]));
|
||||
|
||||
EnvVar {
|
||||
values: Arc::clone(empty_list),
|
||||
@@ -130,7 +130,7 @@ pub fn new(value: WString, flags: EnvVarFlags) -> Self {
|
||||
/// Creates a new `EnvVar`.
|
||||
pub fn new_vec(values: Vec<WString>, flags: EnvVarFlags) -> Self {
|
||||
EnvVar {
|
||||
values: Arc::new(values.into_boxed_slice()),
|
||||
values: values.into(),
|
||||
flags,
|
||||
}
|
||||
}
|
||||
@@ -199,15 +199,15 @@ pub fn get_delimiter(&self) -> char {
|
||||
}
|
||||
|
||||
/// Returns a copy of the variable with new values.
|
||||
pub fn setting_vals(&mut self, values: Vec<WString>) -> Self {
|
||||
pub fn setting_vals(&self, values: Vec<WString>) -> Self {
|
||||
EnvVar {
|
||||
values: Arc::new(values.into_boxed_slice()),
|
||||
values: values.into(),
|
||||
flags: self.flags,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a copy of the variable with the export flag changed.
|
||||
pub fn setting_exports(&mut self, export: bool) -> Self {
|
||||
pub fn setting_exports(&self, export: bool) -> Self {
|
||||
let mut flags = self.flags;
|
||||
flags.set(EnvVarFlags::EXPORT, export);
|
||||
EnvVar {
|
||||
@@ -217,7 +217,7 @@ pub fn setting_exports(&mut self, export: bool) -> Self {
|
||||
}
|
||||
|
||||
/// Returns a copy of the variable with the path variable flag changed.
|
||||
pub fn setting_pathvar(&mut self, pathvar: bool) -> Self {
|
||||
pub fn setting_pathvar(&self, pathvar: bool) -> Self {
|
||||
let mut flags = self.flags;
|
||||
flags.set(EnvVarFlags::PATHVAR, pathvar);
|
||||
EnvVar {
|
||||
|
||||
@@ -522,13 +522,13 @@ fn open_temporary_file(
|
||||
}
|
||||
|
||||
/// Writes our state to the fd. path is provided only for error reporting.
|
||||
fn write_to_fd(&mut self, fd: impl AsFd, path: &wstr) -> std::io::Result<usize> {
|
||||
fn write_to_fd(&mut self, fd: impl AsFd, path: &wstr) -> std::io::Result<()> {
|
||||
let fd = fd.as_fd();
|
||||
let contents = Self::serialize_with_vars(&self.vars);
|
||||
|
||||
let res = write_loop(&fd, &contents);
|
||||
match res.as_ref() {
|
||||
Ok(_) => {
|
||||
Ok(()) => {
|
||||
// Since we just wrote out this file, it matches our internal state; pretend we read from it.
|
||||
self.last_read_file = file_id_for_fd(fd);
|
||||
}
|
||||
@@ -602,9 +602,9 @@ fn generate_callbacks_and_update_exports(
|
||||
let existing = self.vars.get(key);
|
||||
|
||||
// See if the value has changed.
|
||||
let old_exports = existing.map_or(false, |v| v.exports());
|
||||
let old_exports = existing.is_some_and(|v| v.exports());
|
||||
let export_changed = old_exports != new_entry.exports();
|
||||
let value_changed = existing.map_or(false, |v| v != new_entry);
|
||||
let value_changed = existing.is_some_and(|v| v != new_entry);
|
||||
if export_changed || value_changed {
|
||||
self.export_generation += 1;
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ pub fn caller_exit(internal_job_id: u64, job_id: MaybeJobId) -> Self {
|
||||
|
||||
/// Test if specified event is blocked.
|
||||
fn is_blocked(&self, parser: &Parser) -> bool {
|
||||
for block in parser.blocks().iter().rev() {
|
||||
for block in parser.blocks_iter_rev() {
|
||||
if block.event_blocks {
|
||||
return true;
|
||||
}
|
||||
@@ -371,7 +371,7 @@ pub fn is_signal_observed(sig: libc::c_int) -> bool {
|
||||
// We are in a signal handler!
|
||||
OBSERVED_SIGNALS
|
||||
.get(usize::try_from(sig).unwrap())
|
||||
.map_or(false, |s| s.load(Ordering::Relaxed) > 0)
|
||||
.is_some_and(|s| s.load(Ordering::Relaxed) > 0)
|
||||
}
|
||||
|
||||
pub fn get_desc(parser: &Parser, evt: &Event) -> WString {
|
||||
|
||||
@@ -766,7 +766,7 @@ fn create_output_stream_for_builtin(
|
||||
IoMode::bufferfill => {
|
||||
// Our IO redirection is to an internal buffer, e.g. a command substitution.
|
||||
// We will write directly to it.
|
||||
let buffer = io.as_bufferfill().unwrap().buffer_ref();
|
||||
let buffer = io.as_bufferfill().unwrap().buffer();
|
||||
OutputStream::Buffered(BufferedOutputStream::new(buffer.clone()))
|
||||
}
|
||||
IoMode::close => {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#[cfg(not(target_has_atomic = "64"))]
|
||||
use portable_atomic::AtomicU64;
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::prelude::*;
|
||||
#[cfg(target_has_atomic = "64")]
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::common::exit_without_destructors;
|
||||
use crate::fd_readable_set::FdReadableSet;
|
||||
use crate::fd_readable_set::{FdReadableSet, Timeout};
|
||||
use crate::fds::AutoCloseFd;
|
||||
use crate::flog::FLOG;
|
||||
use crate::threads::assert_is_background_thread;
|
||||
@@ -21,17 +22,6 @@
|
||||
#[cfg(HAVE_EVENTFD)]
|
||||
use libc::{EFD_CLOEXEC, EFD_NONBLOCK};
|
||||
|
||||
/// Reason for waking an item
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ItemWakeReason {
|
||||
/// The fd became readable (or was HUP'd)
|
||||
Readable,
|
||||
/// The requested timeout was hit
|
||||
Timeout,
|
||||
/// The item was "poked" (woken up explicitly)
|
||||
Poke,
|
||||
}
|
||||
|
||||
/// An event signaller implemented using a file descriptor, so it can plug into
|
||||
/// [`select()`](libc::select).
|
||||
///
|
||||
@@ -146,27 +136,12 @@ pub fn post(&self) {
|
||||
/// but guarantees that the next call to wait() will not block.
|
||||
/// Return true if readable, false if not readable, or not interrupted by a signal.
|
||||
pub fn poll(&self, wait: bool /* = false */) -> bool {
|
||||
let mut timeout = libc::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
let timeout = if wait {
|
||||
Timeout::Forever
|
||||
} else {
|
||||
Timeout::ZERO
|
||||
};
|
||||
let mut fds: libc::fd_set = unsafe { std::mem::zeroed() };
|
||||
unsafe { libc::FD_ZERO(&mut fds) };
|
||||
unsafe { libc::FD_SET(self.read_fd(), &mut fds) };
|
||||
let res = unsafe {
|
||||
libc::select(
|
||||
self.read_fd() + 1,
|
||||
&mut fds,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
if wait {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
&mut timeout
|
||||
},
|
||||
)
|
||||
};
|
||||
res > 0
|
||||
FdReadableSet::is_fd_readable(self.read_fd(), timeout)
|
||||
}
|
||||
|
||||
/// Return the fd to write to.
|
||||
@@ -178,9 +153,9 @@ fn write_fd(&self) -> RawFd {
|
||||
}
|
||||
}
|
||||
|
||||
/// Each item added to fd_monitor_t is assigned a unique ID, which is not recycled. Items may have
|
||||
/// Each item added to FdMonitor is assigned a unique ID, which is not recycled. Items may have
|
||||
/// their callback triggered immediately by passing the ID. Zero is a sentinel.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FdMonitorItemId(u64);
|
||||
|
||||
impl From<FdMonitorItemId> for u64 {
|
||||
@@ -196,10 +171,9 @@ fn from(value: u64) -> Self {
|
||||
}
|
||||
|
||||
/// The callback type used by [`FdMonitorItem`]. It is passed a mutable reference to the
|
||||
/// `FdMonitorItem`'s [`FdMonitorItem::fd`] and [the reason](ItemWakeupReason) for the wakeup.
|
||||
/// It should return an [`ItemAction`] to indicate whether the item should be removed from the
|
||||
/// [`FdMonitor`] set.
|
||||
pub type Callback = Box<dyn Fn(&mut AutoCloseFd, ItemWakeReason) -> ItemAction + Send + Sync>;
|
||||
/// `FdMonitorItem`'s [`FdMonitorItem::fd`]. If the fd is closed, the callback will not
|
||||
/// be invoked again.
|
||||
pub type Callback = Box<dyn Fn(&mut AutoCloseFd) + Send + Sync>;
|
||||
|
||||
/// An item containing an fd and callback, which can be monitored to watch when it becomes readable
|
||||
/// and invoke the callback.
|
||||
@@ -209,82 +183,20 @@ pub struct FdMonitorItem {
|
||||
/// A callback to be invoked when the fd is readable, or for another reason given by the wake reason.
|
||||
/// If the fd is invalid on return from the function, then the item is removed from the [`FdMonitor`] set.
|
||||
callback: Callback,
|
||||
/// The timeout associated with waiting on this item or `None` to wait indefinitely. A timeout
|
||||
/// of `0` is not supported.
|
||||
timeout: Option<Duration>,
|
||||
/// The last time we were called or the time of initialization.
|
||||
last_time: Option<Instant>,
|
||||
/// The id for this item, assigned by [`FdMonitor`].
|
||||
item_id: FdMonitorItemId,
|
||||
}
|
||||
|
||||
/// A value returned by the callback to indicate what to do with the item.
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum ItemAction {
|
||||
Remove,
|
||||
Retain,
|
||||
}
|
||||
|
||||
impl FdMonitorItem {
|
||||
/// Return the duration until the timeout should trigger or `None`. A return of `0` means we are
|
||||
/// at or past the timeout.
|
||||
fn remaining_time(&self, now: &Instant) -> Option<Duration> {
|
||||
let last_time = self.last_time.expect("Should always have a last_time!");
|
||||
let timeout = self.timeout?;
|
||||
assert!(now >= &last_time, "Steady clock went backwards or bug!");
|
||||
let since = *now - last_time;
|
||||
Some(if since >= timeout {
|
||||
Duration::ZERO
|
||||
} else {
|
||||
timeout - since
|
||||
})
|
||||
}
|
||||
|
||||
/// Invoke this item's callback if its value (when its value is set in the fd or has timed out).
|
||||
/// Returns `true` if the item should be retained or `false` if it should be removed from the
|
||||
/// set.
|
||||
fn service_item(&mut self, fds: &FdReadableSet, now: &Instant) -> ItemAction {
|
||||
let mut result = ItemAction::Retain;
|
||||
let readable = fds.test(self.fd.as_raw_fd());
|
||||
let timed_out = !readable && self.remaining_time(now) == Some(Duration::ZERO);
|
||||
if readable || timed_out {
|
||||
self.last_time = Some(*now);
|
||||
let reason = if readable {
|
||||
ItemWakeReason::Readable
|
||||
} else {
|
||||
ItemWakeReason::Timeout
|
||||
};
|
||||
result = (self.callback)(&mut self.fd, reason);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Invoke this item's callback with a poke, if its id is present in the sorted poke list.
|
||||
fn maybe_poke_item(&mut self, pokelist: &[FdMonitorItemId]) -> ItemAction {
|
||||
if self.item_id.0 == 0 || pokelist.binary_search(&self.item_id).is_err() {
|
||||
// Not pokeable or not in the poke list.
|
||||
return ItemAction::Retain;
|
||||
}
|
||||
|
||||
(self.callback)(&mut self.fd, ItemWakeReason::Poke)
|
||||
}
|
||||
|
||||
pub fn new(fd: AutoCloseFd, timeout: Option<Duration>, callback: Callback) -> Self {
|
||||
FdMonitorItem {
|
||||
fd,
|
||||
timeout,
|
||||
callback,
|
||||
item_id: FdMonitorItemId(0),
|
||||
last_time: None,
|
||||
}
|
||||
/// Invoke this item's callback because the fd is readable.
|
||||
/// Returns the [`ItemAction`] to indicate whether the item should be removed from the [`FdMonitor`] set.
|
||||
fn service(&mut self) {
|
||||
(self.callback)(&mut self.fd)
|
||||
}
|
||||
}
|
||||
|
||||
/// A thread-safe class which can monitor a set of fds, invoking a callback when any becomes
|
||||
/// readable (or has been HUP'd) or when per-item-configurable timeouts are reached.
|
||||
/// readable (or has been HUP'd).
|
||||
pub struct FdMonitor {
|
||||
/// Our self-signaller. When this is written to, it means there are new items pending, new items
|
||||
/// in the poke list, or terminate has been set.
|
||||
/// Our self-signaller, used to wake up the background thread out of select().
|
||||
change_signaller: Arc<FdEventSignaller>,
|
||||
/// The data shared between the background thread and the `FdMonitor` instance.
|
||||
data: Arc<Mutex<SharedData>>,
|
||||
@@ -303,11 +215,8 @@ fn assert_sync<T: Sync>() {}
|
||||
|
||||
/// Data shared between the `FdMonitor` instance and its associated `BackgroundFdMonitor`.
|
||||
struct SharedData {
|
||||
/// Pending items. This is set by the main thread with the mutex locked, then the background
|
||||
/// thread grabs them.
|
||||
pending: Vec<FdMonitorItem>,
|
||||
/// List of IDs for items that need to be poked (explicitly woken up).
|
||||
pokelist: Vec<FdMonitorItemId>,
|
||||
/// The map of items. This may be modified by the main thread with the mutex locked.
|
||||
items: HashMap<FdMonitorItemId, FdMonitorItem>,
|
||||
/// Whether the background thread is running.
|
||||
running: bool,
|
||||
/// Used to signal that the background thread should terminate.
|
||||
@@ -316,35 +225,31 @@ struct SharedData {
|
||||
|
||||
/// The background half of the fd monitor, running on its own thread.
|
||||
struct BackgroundFdMonitor {
|
||||
/// The list of items to monitor. This is only accessed from the background thread.
|
||||
/// This doesn't need to be in any particular order.
|
||||
items: Vec<FdMonitorItem>,
|
||||
/// Our self-signaller. When this is written to, it means there are new items pending, new items
|
||||
/// in the poke list, or terminate has been set.
|
||||
change_signaller: Weak<FdEventSignaller>,
|
||||
change_signaller: Arc<FdEventSignaller>,
|
||||
/// The data shared between the background thread and the `FdMonitor` instance.
|
||||
/// Note the locking here is very coarse and the lock is held while servicing items.
|
||||
/// This means that an item which reads a lot of data may prevent adding other items.
|
||||
/// When we do true multithreaded execution, we may want to make the locking more fine-grained (per-item).
|
||||
data: Arc<Mutex<SharedData>>,
|
||||
}
|
||||
|
||||
impl FdMonitor {
|
||||
/// Add an item to the monitor. Returns the [`FdMonitorItemId`] assigned to the item.
|
||||
pub fn add(&self, mut item: FdMonitorItem) -> FdMonitorItemId {
|
||||
assert!(item.fd.is_valid());
|
||||
assert!(item.timeout != Some(Duration::ZERO), "Invalid timeout!");
|
||||
assert!(
|
||||
item.item_id == FdMonitorItemId(0),
|
||||
"Item should not already have an id!"
|
||||
);
|
||||
pub fn add(&self, fd: AutoCloseFd, callback: Callback) -> FdMonitorItemId {
|
||||
assert!(fd.is_valid());
|
||||
|
||||
let item_id = self.last_id.fetch_add(1, Ordering::Relaxed) + 1;
|
||||
let item_id = FdMonitorItemId(item_id);
|
||||
let item: FdMonitorItem = FdMonitorItem { fd, callback };
|
||||
let start_thread = {
|
||||
// Lock around a local region
|
||||
let mut data = self.data.lock().expect("Mutex poisoned!");
|
||||
|
||||
// Assign an id and add the item to pending
|
||||
item.item_id = item_id;
|
||||
data.pending.push(item);
|
||||
// Assign an id and add the item.
|
||||
let old_value = data.items.insert(item_id, item);
|
||||
assert!(old_value.is_none(), "Item ID {} already exists!", item_id.0);
|
||||
|
||||
// Start the thread if it hasn't already been started
|
||||
let already_started = data.running;
|
||||
@@ -356,8 +261,7 @@ pub fn add(&self, mut item: FdMonitorItem) -> FdMonitorItemId {
|
||||
FLOG!(fd_monitor, "Thread starting");
|
||||
let background_monitor = BackgroundFdMonitor {
|
||||
data: Arc::clone(&self.data),
|
||||
change_signaller: Arc::downgrade(&self.change_signaller),
|
||||
items: Vec::new(),
|
||||
change_signaller: Arc::clone(&self.change_signaller),
|
||||
};
|
||||
crate::threads::spawn(move || {
|
||||
background_monitor.run();
|
||||
@@ -370,29 +274,24 @@ pub fn add(&self, mut item: FdMonitorItem) -> FdMonitorItemId {
|
||||
item_id
|
||||
}
|
||||
|
||||
/// Mark that the item with the given ID needs to be woken up explicitly.
|
||||
pub fn poke_item(&self, item_id: FdMonitorItemId) {
|
||||
/// Remove an item from the monitor and return its file descriptor.
|
||||
/// Note we may remove an item whose fd is currently being waited on in select(); this is
|
||||
/// considered benign because the underlying item will no longer be present and so its
|
||||
/// callback will not be invoked.
|
||||
pub fn remove_item(&self, item_id: FdMonitorItemId) -> AutoCloseFd {
|
||||
assert!(item_id.0 > 0, "Invalid item id!");
|
||||
let needs_notification = {
|
||||
let mut data = self.data.lock().expect("Mutex poisoned!");
|
||||
let needs_notification = data.pokelist.is_empty();
|
||||
// Insert it, sorted. But not if it already exists.
|
||||
if let Err(pos) = data.pokelist.binary_search(&item_id) {
|
||||
data.pokelist.insert(pos, item_id);
|
||||
};
|
||||
needs_notification
|
||||
};
|
||||
|
||||
if needs_notification {
|
||||
self.change_signaller.post();
|
||||
}
|
||||
let mut data = self.data.lock().expect("Mutex poisoned!");
|
||||
let removed = data.items.remove(&item_id).expect("Item ID not found");
|
||||
drop(data);
|
||||
// Allow it to recompute the wait set.
|
||||
self.change_signaller.post();
|
||||
removed.fd
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Arc::new(Mutex::new(SharedData {
|
||||
pending: Vec::new(),
|
||||
pokelist: Vec::new(),
|
||||
items: HashMap::new(),
|
||||
running: false,
|
||||
terminate: false,
|
||||
})),
|
||||
@@ -405,110 +304,121 @@ pub fn new() -> Self {
|
||||
impl BackgroundFdMonitor {
|
||||
/// Starts monitoring the fd set and listening for new fds to add to the set. Takes ownership
|
||||
/// over its instance so that this method cannot be called again.
|
||||
fn run(mut self) {
|
||||
fn run(self) {
|
||||
assert_is_background_thread();
|
||||
|
||||
let mut pokelist: Vec<FdMonitorItemId> = Vec::new();
|
||||
let mut fds = FdReadableSet::new();
|
||||
let mut item_ids: Vec<FdMonitorItemId> = Vec::new();
|
||||
|
||||
loop {
|
||||
// Poke any items that need it
|
||||
if !pokelist.is_empty() {
|
||||
self.poke(&pokelist);
|
||||
pokelist.clear();
|
||||
}
|
||||
fds.clear();
|
||||
// Our general flow is that a client thread adds an item for us to monitor,
|
||||
// and we use select() or poll() to wait on it. However, the client thread
|
||||
// may then reclaim the item. We are currently blocked in select():
|
||||
// how then do we stop waiting on it?
|
||||
//
|
||||
// The safest, slowest approach is:
|
||||
// - The background thread waits on select() for the set of active file descriptors.
|
||||
// - The client thread records a request to remove an item.
|
||||
// - The client thread wakes up the background thread via change_signaller.
|
||||
// - The background thread check for any pending removals, and removes and returns them.
|
||||
// - The client thread accepts the removed item and continues on.
|
||||
// However this means a round-trip from the client thread to this background thread,
|
||||
// plus additional blocking system calls. This slows down the client thread.
|
||||
//
|
||||
// A second possibility is that:
|
||||
// - The background thread waits on select() for the set of active file descriptors.
|
||||
// - The client thread directly removes an item (protected by the mutex).
|
||||
// - After select() returns the set of active file descriptors, we only invoke callbacks
|
||||
// for items whose file descriptors are still in the set.
|
||||
// However this risks the ABA problem: if the client thread reclaims an item, closes its
|
||||
// fd, and then adds a new item which happens to get the same fd, we might falsely
|
||||
// trigger the callback of the new item even though its fd is not readable.
|
||||
//
|
||||
// So we use the following approach:
|
||||
// - The background thread creates a snapshotted list of active ItemIDs.
|
||||
// - The background thread waits in select() on the set of active file descriptors,
|
||||
// without holding the lock.
|
||||
// - The client thread directly removes an item (protected by the mutex).
|
||||
// - After select() returns the set of active file descriptors, we only invoke callbacks
|
||||
// for items whose file descriptors are marked active, and whose ItemID was snapshotted.
|
||||
//
|
||||
// This avoids the ABA problem because ItemIDs are never recycled. It does have a race where
|
||||
// we might select() on a file descriptor that has been closed or recycled. Thus we must be
|
||||
// prepared to handle EBADF. This race is otherwise considered benign.
|
||||
|
||||
// Our change_signaller is special-cased
|
||||
let change_signal_fd = self.change_signaller.upgrade().unwrap().read_fd();
|
||||
// Construct the set of fds to monitor.
|
||||
// Our change_signaller is special-cased.
|
||||
fds.clear();
|
||||
let change_signal_fd = self.change_signaller.read_fd();
|
||||
fds.add(change_signal_fd);
|
||||
|
||||
let mut now = Instant::now();
|
||||
// Use Duration::MAX to represent no timeout for comparison purposes.
|
||||
let mut timeout = Duration::MAX;
|
||||
|
||||
for item in &mut self.items {
|
||||
fds.add(item.fd.as_raw_fd());
|
||||
if item.last_time.is_none() {
|
||||
item.last_time = Some(now);
|
||||
// Grab the lock and snapshot the item_ids. Skip items with invalid fds.
|
||||
let mut data = self.data.lock().expect("Mutex poisoned!");
|
||||
item_ids.clear();
|
||||
item_ids.reserve(data.items.len());
|
||||
for (item_id, item) in &data.items {
|
||||
let fd = item.fd.as_raw_fd();
|
||||
if fd >= 0 {
|
||||
fds.add(fd);
|
||||
item_ids.push(*item_id);
|
||||
}
|
||||
timeout = timeout.min(item.timeout.unwrap_or(Duration::MAX));
|
||||
}
|
||||
|
||||
// Sort it to avoid the non-determinism of the hash table.
|
||||
item_ids.sort_unstable();
|
||||
|
||||
// If we have no items, then we wish to allow the thread to exit, but after a time, so
|
||||
// we aren't spinning up and tearing down the thread repeatedly. Set a timeout of 256
|
||||
// msec; if nothing becomes readable by then we will exit. We refer to this as the
|
||||
// wait-lap.
|
||||
let is_wait_lap = self.items.is_empty();
|
||||
if is_wait_lap {
|
||||
assert!(
|
||||
timeout == Duration::MAX,
|
||||
"Should not have a timeout on wait lap!"
|
||||
);
|
||||
timeout = Duration::from_millis(256);
|
||||
}
|
||||
|
||||
// Don't leave Duration::MAX as an actual timeout value
|
||||
let timeout = match timeout {
|
||||
Duration::MAX => None,
|
||||
timeout => Some(timeout),
|
||||
let is_wait_lap = item_ids.is_empty();
|
||||
let timeout = if is_wait_lap {
|
||||
Some(Duration::from_millis(256))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Call select()
|
||||
// Call select().
|
||||
// We must release and then re-acquire the lock around select() to avoid deadlock.
|
||||
// Note that while we are waiting in select(), the client thread may add or remove items;
|
||||
// in particular it may even close file descriptors that we are waiting on. That is why
|
||||
// we handle EBADF. Note that even if the file descriptor is recycled, we don't invoke
|
||||
// a callback for it unless its ItemID is still present.
|
||||
drop(data);
|
||||
let ret = fds.check_readable(
|
||||
timeout
|
||||
.map(|duration| duration.as_micros() as u64)
|
||||
.unwrap_or(FdReadableSet::kNoTimeout),
|
||||
.map(|d| Timeout::Duration(d))
|
||||
.unwrap_or(Timeout::Forever),
|
||||
);
|
||||
if ret < 0 && errno::errno().0 != libc::EINTR {
|
||||
if ret < 0 && !matches!(errno().0, libc::EINTR | libc::EBADF) {
|
||||
// Surprising error
|
||||
perror("select");
|
||||
}
|
||||
|
||||
// Update the value of `now` after waiting on `fds.check_readable()`; it's used in the
|
||||
// servicer closure.
|
||||
now = Instant::now();
|
||||
// Re-acquire the lock.
|
||||
data = self.data.lock().expect("Mutex poisoned!");
|
||||
|
||||
// A predicate which services each item in turn, returning true if it should be removed
|
||||
let servicer = |item: &mut FdMonitorItem| {
|
||||
let fd = item.fd.as_raw_fd();
|
||||
let action = item.service_item(&fds, &now);
|
||||
if action == ItemAction::Remove {
|
||||
FLOG!(fd_monitor, "Removing fd", fd);
|
||||
// For each item id that we snapshotted, if the corresponding item is still in our
|
||||
// set of active items and its fd was readable, then service it.
|
||||
for item_id in &item_ids {
|
||||
let Some(item) = data.items.get_mut(item_id) else {
|
||||
// Item was removed while we were waiting.
|
||||
// Note there is no risk of an ABA problem because ItemIDs are never recycled.
|
||||
continue;
|
||||
};
|
||||
if fds.test(item.fd.as_raw_fd()) {
|
||||
item.service();
|
||||
}
|
||||
action
|
||||
};
|
||||
|
||||
// Service all items that are either readable or have timed out, and remove any which
|
||||
// say to do so.
|
||||
|
||||
self.items
|
||||
.retain_mut(|item| servicer(item) == ItemAction::Retain);
|
||||
}
|
||||
|
||||
// Handle any changes if the change signaller was set. Alternatively, this may be the
|
||||
// wait lap, in which case we might want to commit to exiting.
|
||||
let change_signalled = fds.test(change_signal_fd);
|
||||
if change_signalled || is_wait_lap {
|
||||
// Clear the change signaller before processing incoming changes
|
||||
self.change_signaller.upgrade().unwrap().try_consume();
|
||||
let mut data = self.data.lock().expect("Mutex poisoned!");
|
||||
self.change_signaller.try_consume();
|
||||
|
||||
// Move from `pending` to the end of `items`
|
||||
self.items.extend(&mut data.pending.drain(..));
|
||||
|
||||
// Grab any poke list
|
||||
assert!(
|
||||
pokelist.is_empty(),
|
||||
"poke list should be empty or else we're dropping pokes!"
|
||||
);
|
||||
std::mem::swap(&mut pokelist, &mut data.pokelist);
|
||||
|
||||
if data.terminate
|
||||
|| (is_wait_lap
|
||||
&& self.items.is_empty()
|
||||
&& pokelist.is_empty()
|
||||
&& !change_signalled)
|
||||
{
|
||||
if data.terminate || (is_wait_lap && data.items.is_empty() && !change_signalled) {
|
||||
// Maybe terminate is set. Alternatively, maybe we had no items, waited a bit,
|
||||
// and still have no items. It's important to do this while holding the lock,
|
||||
// otherwise we race with new items being added.
|
||||
@@ -523,27 +433,12 @@ fn run(mut self) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Poke items in the poke list, removing any items that close their fd in their callback. The
|
||||
/// poke list is consumed after this. This is only called from the background thread.
|
||||
fn poke(&mut self, pokelist: &[FdMonitorItemId]) {
|
||||
self.items.retain_mut(|item| {
|
||||
let action = item.maybe_poke_item(pokelist);
|
||||
if action == ItemAction::Remove {
|
||||
FLOG!(fd_monitor, "Removing fd", item.fd.as_raw_fd());
|
||||
}
|
||||
return action == ItemAction::Retain;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// In ordinary usage, we never invoke the destructor. This is used in the tests to not leave stale
|
||||
/// fds arounds; this is why it's very hacky!
|
||||
impl Drop for FdMonitor {
|
||||
fn drop(&mut self) {
|
||||
// Safety: this is a port of the C++ code and we are running in the destructor. The C++ code
|
||||
// had no way to bubble back any errors encountered here, and the pthread mutex the C++ code
|
||||
// uses does not have a concept of mutex poisoning.
|
||||
self.data.lock().expect("Mutex poisoned!").terminate = true;
|
||||
self.change_signaller.post();
|
||||
|
||||
|
||||
@@ -1,10 +1,36 @@
|
||||
use libc::c_int;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::time::Duration;
|
||||
|
||||
pub enum Timeout {
|
||||
Duration(Duration),
|
||||
Forever,
|
||||
}
|
||||
|
||||
impl Timeout {
|
||||
pub const ZERO: Timeout = Timeout::Duration(Duration::ZERO);
|
||||
|
||||
/// Convert from usecs to poll-friendly msecs.
|
||||
#[allow(unused)]
|
||||
fn as_poll_msecs(&self) -> c_int {
|
||||
match self {
|
||||
// Negative values mean wait forever in poll-speak.
|
||||
Timeout::Forever => -1 as c_int,
|
||||
Timeout::Duration(duration) => {
|
||||
assert!(
|
||||
duration.as_millis() < c_int::MAX as _,
|
||||
"Timeout too long but not forever!"
|
||||
);
|
||||
duration.as_millis() as c_int
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the fd is or becomes readable within the given timeout.
|
||||
/// This returns `false` if the waiting is interrupted by a signal.
|
||||
pub fn is_fd_readable(fd: i32, timeout_usec: u64) -> bool {
|
||||
FdReadableSet::is_fd_readable(fd, timeout_usec)
|
||||
pub fn is_fd_readable(fd: i32, timeout: Timeout) -> bool {
|
||||
FdReadableSet::is_fd_readable(fd, timeout)
|
||||
}
|
||||
|
||||
/// Returns whether an fd is readable.
|
||||
@@ -23,11 +49,6 @@ pub struct FdReadableSet {
|
||||
nfds_: c_int,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
const kUsecPerMsec: u64 = 1000;
|
||||
#[allow(dead_code)]
|
||||
const kUsecPerSec: u64 = 1000 * kUsecPerMsec;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl FdReadableSet {
|
||||
/// Construct an empty set.
|
||||
@@ -64,51 +85,43 @@ pub fn test(&self, fd: RawFd) -> bool {
|
||||
fd >= 0 && unsafe { libc::FD_ISSET(fd, &self.fdset_) }
|
||||
}
|
||||
|
||||
/// Call `select()` or `poll()`, according to FISH_READABLE_SET_USE_POLL. Note this
|
||||
/// destructively modifies the set. Returns the result of `select()` or `poll()`.
|
||||
pub fn check_readable(&mut self, timeout_usec: u64) -> c_int {
|
||||
/// Call `select()`. Note this destructively modifies the set. Returns the result of
|
||||
/// `select()`.
|
||||
pub fn check_readable(&mut self, timeout: Timeout) -> c_int {
|
||||
let null = std::ptr::null_mut();
|
||||
if timeout_usec == Self::kNoTimeout {
|
||||
unsafe {
|
||||
return libc::select(
|
||||
self.nfds_,
|
||||
&mut self.fdset_,
|
||||
null,
|
||||
null,
|
||||
std::ptr::null_mut(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let mut tvs = libc::timeval {
|
||||
tv_sec: (timeout_usec / kUsecPerSec) as libc::time_t,
|
||||
tv_usec: (timeout_usec % kUsecPerSec) as libc::suseconds_t,
|
||||
};
|
||||
unsafe {
|
||||
return libc::select(self.nfds_, &mut self.fdset_, null, null, &mut tvs);
|
||||
let mut tvs;
|
||||
let timeout = match timeout {
|
||||
Timeout::Forever => std::ptr::null_mut(),
|
||||
Timeout::Duration(duration) => {
|
||||
tvs = libc::timeval {
|
||||
tv_sec: duration.as_secs() as libc::time_t,
|
||||
tv_usec: duration.subsec_micros() as libc::suseconds_t,
|
||||
};
|
||||
&mut tvs
|
||||
}
|
||||
};
|
||||
unsafe {
|
||||
return libc::select(self.nfds_, &mut self.fdset_, null, null, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a single fd is readable, with a given timeout.
|
||||
/// Returns `true` if readable, `false` otherwise.
|
||||
pub fn is_fd_readable(fd: RawFd, timeout_usec: u64) -> bool {
|
||||
pub fn is_fd_readable(fd: RawFd, timeout: Timeout) -> bool {
|
||||
if fd < 0 {
|
||||
return false;
|
||||
}
|
||||
let mut s = Self::new();
|
||||
s.add(fd);
|
||||
let res = s.check_readable(timeout_usec);
|
||||
let res = s.check_readable(timeout);
|
||||
return res > 0 && s.test(fd);
|
||||
}
|
||||
|
||||
/// Check if a single fd is readable, without blocking.
|
||||
/// Returns `true` if readable, `false` if not.
|
||||
pub fn poll_fd_readable(fd: RawFd) -> bool {
|
||||
return Self::is_fd_readable(fd, 0);
|
||||
return Self::is_fd_readable(fd, Timeout::ZERO);
|
||||
}
|
||||
|
||||
/// A special timeout value which may be passed to indicate no timeout.
|
||||
pub const kNoTimeout: u64 = u64::MAX;
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
@@ -168,46 +181,29 @@ pub fn test(&self, fd: RawFd) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Convert from usecs to poll-friendly msecs.
|
||||
fn usec_to_poll_msec(timeout_usec: u64) -> c_int {
|
||||
let mut timeout_msec: u64 = timeout_usec / kUsecPerMsec;
|
||||
// Round to nearest, down for halfway.
|
||||
if (timeout_usec % kUsecPerMsec) > kUsecPerMsec / 2 {
|
||||
timeout_msec += 1;
|
||||
}
|
||||
if timeout_usec == FdReadableSet::kNoTimeout || timeout_msec > c_int::MAX as u64 {
|
||||
// Negative values mean wait forever in poll-speak.
|
||||
return -1;
|
||||
}
|
||||
return timeout_msec as c_int;
|
||||
}
|
||||
|
||||
fn do_poll(fds: &mut [libc::pollfd], timeout_usec: u64) -> c_int {
|
||||
fn do_poll(fds: &mut [libc::pollfd], timeout: Timeout) -> c_int {
|
||||
let count = fds.len();
|
||||
assert!(count <= libc::nfds_t::MAX as usize, "count too big");
|
||||
return unsafe {
|
||||
libc::poll(
|
||||
fds.as_mut_ptr(),
|
||||
count as libc::nfds_t,
|
||||
Self::usec_to_poll_msec(timeout_usec),
|
||||
timeout.as_poll_msecs(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// Call select() or poll(), according to FISH_READABLE_SET_USE_POLL. Note this destructively
|
||||
/// modifies the set. Return the result of select() or poll().
|
||||
///
|
||||
/// TODO: Change to [`Duration`](std::time::Duration) once FFI usage is done.
|
||||
pub fn check_readable(&mut self, timeout_usec: u64) -> c_int {
|
||||
/// Call poll(). Note this destructively modifies the set. Return the result of poll().
|
||||
pub fn check_readable(&mut self, timeout: Timeout) -> c_int {
|
||||
if self.pollfds_.is_empty() {
|
||||
return 0;
|
||||
}
|
||||
return Self::do_poll(&mut self.pollfds_, timeout_usec);
|
||||
return Self::do_poll(&mut self.pollfds_, timeout);
|
||||
}
|
||||
|
||||
/// Check if a single fd is readable, with a given timeout.
|
||||
/// Return true if `fd` is our set and is readable, `false` otherwise.
|
||||
pub fn is_fd_readable(fd: RawFd, timeout_usec: u64) -> bool {
|
||||
pub fn is_fd_readable(fd: RawFd, timeout: Timeout) -> bool {
|
||||
if fd < 0 {
|
||||
return false;
|
||||
}
|
||||
@@ -216,16 +212,13 @@ pub fn is_fd_readable(fd: RawFd, timeout_usec: u64) -> bool {
|
||||
events: libc::POLLIN,
|
||||
revents: 0,
|
||||
};
|
||||
let ret = Self::do_poll(std::slice::from_mut(&mut pfd), timeout_usec);
|
||||
let ret = Self::do_poll(std::slice::from_mut(&mut pfd), timeout);
|
||||
return ret > 0 && (pfd.revents & libc::POLLIN) != 0;
|
||||
}
|
||||
|
||||
/// Check if a single fd is readable, without blocking.
|
||||
/// Return true if readable, false if not.
|
||||
pub fn poll_fd_readable(fd: RawFd) -> bool {
|
||||
return Self::is_fd_readable(fd, 0);
|
||||
return Self::is_fd_readable(fd, Timeout::ZERO);
|
||||
}
|
||||
|
||||
/// A special timeout value which may be passed to indicate no timeout.
|
||||
pub const kNoTimeout: u64 = u64::MAX;
|
||||
}
|
||||
|
||||
11
src/fds.rs
11
src/fds.rs
@@ -5,7 +5,6 @@
|
||||
use crate::tests::prelude::*;
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wutil::perror;
|
||||
use errno::{errno, set_errno};
|
||||
use libc::{c_int, EINTR, FD_CLOEXEC, F_GETFD, F_GETFL, F_SETFD, F_SETFL, O_NONBLOCK};
|
||||
use nix::fcntl::FcntlArg;
|
||||
use nix::{fcntl::OFlag, unistd};
|
||||
@@ -23,11 +22,8 @@
|
||||
/// A sentinel value indicating no timeout.
|
||||
pub const NO_TIMEOUT: u64 = u64::MAX;
|
||||
|
||||
/// A helper type for managing and automatically closing a file descriptor
|
||||
///
|
||||
/// This was implemented in rust as a port of the existing C++ code but it didn't take its place
|
||||
/// (yet) and there's still the original cpp implementation in `src/fds.h`, so its name is
|
||||
/// disambiguated because some code uses a mix of both for interop purposes.
|
||||
/// A helper type for managing and automatically closing a file descriptor.
|
||||
/// Importantly this supports an invalid state with an fd of -1.
|
||||
pub struct AutoCloseFd {
|
||||
fd_: RawFd,
|
||||
}
|
||||
@@ -237,8 +233,6 @@ pub fn wopen_cloexec(
|
||||
pub fn open_cloexec(path: &CStr, flags: OFlag, mode: nix::sys::stat::Mode) -> nix::Result<File> {
|
||||
// Port note: the C++ version of this function had a fallback for platforms where
|
||||
// O_CLOEXEC is not supported, using fcntl. In 2023, this is no longer needed.
|
||||
let saved_errno = errno();
|
||||
errno::set_errno(errno::Errno(0));
|
||||
// We retry this in case of signals,
|
||||
// if we get EINTR and it's not a SIGINT, we continue.
|
||||
// If it is that's our cancel signal, so we abort.
|
||||
@@ -247,7 +241,6 @@ pub fn open_cloexec(path: &CStr, flags: OFlag, mode: nix::sys::stat::Mode) -> ni
|
||||
let ret = ret.map(|raw_fd| unsafe { File::from_raw_fd(raw_fd) });
|
||||
match ret {
|
||||
Ok(file) => {
|
||||
set_errno(saved_errno);
|
||||
return Ok(file);
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
@@ -93,7 +93,7 @@ fn allow_autoload(&self, name: &wstr) -> bool {
|
||||
// tombstoned.
|
||||
let props = self.get_props(name);
|
||||
let has_explicit_func =
|
||||
props.map_or(false, |p: Arc<FunctionProperties>| !p.is_autoload.load());
|
||||
props.is_some_and(|p: Arc<FunctionProperties>| !p.is_autoload.load());
|
||||
let tombstoned = self.autoload_tombstones.contains(name);
|
||||
!has_explicit_func && !tombstoned
|
||||
}
|
||||
@@ -108,9 +108,6 @@ fn allow_autoload(&self, name: &wstr) -> bool {
|
||||
})
|
||||
});
|
||||
|
||||
// Safety: global lock.
|
||||
unsafe impl Send for FunctionSet {}
|
||||
|
||||
/// Make sure that if the specified function is a dynamically loaded function, it has been fully
|
||||
/// loaded. Note this executes fish script code.
|
||||
pub fn load(name: &wstr, parser: &Parser) -> bool {
|
||||
|
||||
@@ -27,6 +27,9 @@ pub enum FeatureFlag {
|
||||
|
||||
/// Remove `test`'s one and zero arg mode (make `test -n` return false etc)
|
||||
test_require_arg,
|
||||
|
||||
/// Buffered enter (typed wile running a command) does not execute.
|
||||
buffered_enter_noexec,
|
||||
}
|
||||
|
||||
struct Features {
|
||||
@@ -107,6 +110,14 @@ pub struct FeatureMetadata {
|
||||
default_value: false,
|
||||
read_only: false,
|
||||
},
|
||||
FeatureMetadata {
|
||||
flag: FeatureFlag::buffered_enter_noexec,
|
||||
name: L!("buffered-enter-noexec"),
|
||||
groups: L!("4.1"),
|
||||
description: L!("enter typed while executing will not execute"),
|
||||
default_value: false,
|
||||
read_only: false,
|
||||
},
|
||||
];
|
||||
|
||||
thread_local!(
|
||||
@@ -168,6 +179,7 @@ const fn new() -> Self {
|
||||
AtomicBool::new(METADATA[3].default_value),
|
||||
AtomicBool::new(METADATA[4].default_value),
|
||||
AtomicBool::new(METADATA[5].default_value),
|
||||
AtomicBool::new(METADATA[6].default_value),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user