Compare commits

..

1 Commits

Author SHA1 Message Date
Johannes Altmanninger
a590e1ee1b Turn off release-mode overflow checks again
Not sure about this but "no overflow checks" is the status quo, so
if we want to keep the checks beyond the port, this should be stated
explicitly, as implied by e616de544 (Enable rust overflow checks in
release mode, at least for now, 2023-02-20).
2024-10-23 14:13:10 +02:00
157 changed files with 1252 additions and 3083 deletions

8
.cargo/config.toml Normal file
View File

@@ -0,0 +1,8 @@
# For macOS, support 10.9 on x86-64, and 11.0 on aarch64, which
# is the minimum supported version for Apple Silicon.
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-arg=-mmacosx-version-min=10.9"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-arg=-mmacosx-version-min=11.0"]

View File

@@ -31,11 +31,3 @@ jobs:
# This used to have --deny=warnings, but that turns rust release day
# into automatic CI failure day, so we don't do that.
run: cargo clippy --workspace --all-targets
# Disabling for now because it also checks "advisories",
# making CI fail for reasons unrelated to the patch
# cargo-deny:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: EmbarkStudios/cargo-deny-action@v1

View File

@@ -1,47 +0,0 @@
name: staticbuilds
on:
# release:
# types: [published]
# schedule:
# - cron: "14 13 * * *"
workflow_dispatch:
env:
CTEST_PARALLEL_LEVEL: "1"
CMAKE_BUILD_PARALLEL_LEVEL: "4"
jobs:
staticbuilds:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: dtolnay/rust-toolchain@1.70
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prepare
run: |
sudo apt install python3-sphinx
rustup target add x86_64-unknown-linux-musl
rustup target add aarch64-unknown-linux-musl
sudo apt install musl-tools crossbuild-essential-arm64 -y
- name: Build
run: |
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2" CMAKE_WITH_GETTEXT=0 CC=aarch64-linux-gnu-gcc RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-lgcc -C link-arg=-D_FORTIFY_SOURCE=0" cargo build --release --target aarch64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
- name: Compress
run: |
tar -cazf fish-amd64.tar.xz -C target/x86_64-unknown-linux-musl/release/ fish{,_indent,_key_reader}
tar -cazf fish-aarch64.tar.xz -C target/aarch64-unknown-linux-musl/release/ fish{,_indent,_key_reader}
- uses: actions/upload-artifact@v4
with:
name: fish
path: |
fish-amd64.tar.xz
fish-aarch64.tar.xz
retention-days: 14

View File

@@ -1,11 +1,25 @@
fish 4.0b1 (released December 17, 2024)
=======================================
fish 3.8.0 (released ???)
===================================
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: 9439 9440 9442 9452 9469 9480 9482 9520 9536 9541 9544 9559 9561 9576 9575
9568 9588 9556 9563 9567 9585 9591 9593 9594 9603 9600 9599 9592 9612 9613 9619 9630 9638 9625 9654 9637 9673 9666 9626 9688 9725 9636 9735
9542 9332 9566 9573 9579 9586 9589 9607 9608 9615 9616 9621 9641 9642 9643 9653 9658 9661
9671 9726 9729 9739 9745 9746 9754 9765 9767 9768 9771 9777 9778 9786 9816 9818 9821 9839
9845 9864 9916 9923 9962 9963 9990 9991 10121 10179 9856 9859 9861 9863 9867 9869 9874 9879
9881 9894 9896 9902 9873 10228 9925 9928 10145 10146 10161 10173 10195 10220 10222 10288
10342 10358 9927 9930 9947 9948 9950 9952 9966 9968 9980 9981 9984 10040 10061 10101 10090
10102 10108 10114 10115 10128 10129 10143 10174 10175 10180 10182 10184 10185 10186 10188
10198 10200 10201 10204 10210 10214 10219 10223 10227 10232 10235 10237 10243 10244 10245
10246 10251 10260 10267 10281 10347 10366 10368 10370 10371 10263 10270 10272 10276 10277
10278 10279 10291 10293 10305 10306 10309 10316 10317 10327 10328 10329 10330 10336 10340
10345 10346 10353 10354 10356 10372 10373 3299 10360 10359
2037 2037 3017 3018 3162 4865 5284 5991 6981 6996 7172 9751 9893 10241 10254 10268 10290 10307
10308 10321 10338 10348 10349 10355 10357 10379 10381 10388 10389 10390 10395 10398 10400 10403
10404 10407 10408 10409 10411 10412 10417 10418 10427 10429 10438 10439 10440 10441 10442 10443
10445 10448 10450 10451 10456 10457 10462 10463 10464 10466 10467 10474 10481 10490 10492 10494
10499 10503 10505 10508 10509 10510 10511 10512 10513 10518 10547 10719
.. 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
fish's core code has been ported from C++ to Rust (:issue:`9512`).
The entirety of fish's C++ code has been ported to Rust (:issue:`9512`).
This means a large change in dependencies and how to build fish.
Packagers should see the :ref:`For Distributors <rust-packaging>` section at the end.
@@ -15,35 +29,36 @@ Notable backwards-incompatible changes
- As part of a larger binding rework, ``bind`` gained a new key notation.
In most cases the old notation should keep working, but in rare cases you may have to change a ``bind`` invocation to use the new notation.
See :ref:`below <changelog-new-bindings>` for details.
- 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.
- Fish no longer supports terminals that fail to ignore OSC or CSI sequences they don't recognize.
The typical problem is that terminals echo the raw sequences sent by fish instead of silently ignoring them.
- :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).
- ``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.
- ``random`` now uses a different random number generator and so the values you get even with the same seed have changed.
Notably, it will now work much more sensibly with very small seeds.
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`).
- Variables in command position that expand to a subcommand keyword are now forbidden to fix a likely user error.
For example, ``set editor command emacs; $editor`` is no longer allowed (:issue:`10249`).
- ``functions --handlers`` will now list handlers in a different order.
Now it is definition order, first to last, where before it was last to first.
This was never specifically defined, and we recommend not relying on a specific order (:issue:`9944`).
- The ``qmark-noglob`` feature, introduced in fish 3.0, is enabled by default. That means ``?`` will no longer act as a single-character glob.
- The ``qmark-noglob`` feature flag, introduced in fish 3.0, is now turned on by default. That means ``?`` will no longer act as a single-character glob.
You can, for the time being, turn it back on by adding ``no-qmark-noglob`` to :envvar:`fish_features` and restarting fish::
set -Ua fish_features no-qmark-noglob
The flag will eventually be made read-only, making it impossible to turn off.
- fish no longer searches directories from the Windows system/user ``$PATH`` environment variable for Linux executables. To execute Linux binaries by name (i.e. not with a relative or absolute path) from a Windows folder, make sure the ``/mnt/c/...`` path is explicitly added to ``$fish_user_paths`` and not just automatically appended to ``$PATH`` by ``wsl.exe`` (:issue:`10506`).
- Under Microsoft Windows Subsystem for Linux 1 (not WSL 2) , backgrounded jobs that have not been disowned and do not terminate on their own after a ``SIGHUP`` + ``SIGCONT`` sequence will be explicitly killed by fish on exit (after the usual prompt to close or disown them) to work around a WSL 1 deficiency that sees backgrounded processes that run into ``SIGTTOU`` remain in a suspended state indefinitely (:issue:`5263`). The workaround is to explicitly ``disown`` processes you wish to outlive the shell session.
- Fish no longer searches directories from the Windows system/user ``$PATH`` environment variable for Linux executables. To execute Linux binaries by name (i.e. not with a relative or absolute path) from a Windows folder, make sure the ``/mnt/c/...`` path is explicitly added to ``$fish_user_paths`` and not just automatically appended to ``$PATH`` by ``wsl.exe`` (:issue:`10506`).
- Under WSLv1, backgrounded jobs that have not been disowned and do not terminate on their own after a ``SIGHUP`` + ``SIGCONT`` sequence will be explicitly killed by fish on exit/exec (after the usual prompt to close or disown them) to work around a WSL deficiency that sees backgrounded processes that run into ``SIGTTOU`` remain in a suspended state indefinitely (:issue:`5263`). The workaround is to explicitly ``disown`` processes you wish to outlive the shell session.
Notable improvements and fixes
------------------------------
.. _changelog-new-bindings:
- fish now requests XTerm's ``modifyOtherKeys`` keyboard encoding and `kitty keyboard protocol's <https://sw.kovidgoyal.net/kitty/keyboard-protocol/>`_ progressive enhancements (:issue:`10359`).
Depending on terminal support, this allows to binding more key combinations, including arbitrary combinations of modifiers :kbd:`ctrl`, :kbd:`alt` and :kbd:`shift`, and distinguishing (for example) :kbd:`ctrl-i` from :kbd:`tab`.
- fish now requests XTerm's ``modifyOtherKeys`` keyboard encoding and `kitty keyboard protocol's <https://sw.kovidgoyal.net/kitty/keyboard-protocol/>`_ progressive enhancements for a CSI u encoding.
Depending on terminal support, this allows to bind a lot more key combinations, including arbitrary combinations of modifiers :kbd:`ctrl`, :kbd:`alt` and :kbd:`shift`,
and to distinguish e.g. :kbd:`ctrl-i` from :kbd:`tab`.
Additionally, ``bind`` now supports a human-readable syntax in addition to byte sequences.
Additionally, builtin ``bind`` no longer requires specifying keys as byte sequences but learned a human-readable syntax.
This includes modifier names, and names for keys like :kbd:`enter` and :kbd:`backspace`.
For example
@@ -51,142 +66,72 @@ Notable improvements and fixes
- ``bind ctrl-x,alt-c 'do something'`` binds a sequence of two keys.
Any key argument that starts with an ASCII control character (like ``\e`` or ``\cX``) or is up to 3 characters long, not a named key, and does not contain ``,`` or ``-`` will be interpreted in the old syntax to keep compatibility for the majority of bindings.
- fish can now be built as a self-installing binary (:issue:`10367`). That means it can be easily built on one system and copied to another, where it can extract supporting files.
To do this, run::
cargo install --path . # in a clone of the fish repository
# or `cargo build --release` and copy target/release/fish{,_indent,_key_reader} wherever you want
The first time it runs interactively, it will extract all the data files to ``~/.local/share/fish/install/``. To uninstall, remove the fish binaries and that directory.
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 ``bind ctrl-c __fish_cancel_commandline`` (: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`).
- Special input functions run from bindings via ``commandline -f`` are now applied immediately instead of after the currently executing binding.
For example, ``commandline -i foo; commandline | grep foo`` succeeds now.
- Undo history is no longer truncated after every command, but kept for the lifetime of the shell process.
- Undo history is no longer truncated after every command but kept for the lifetime of the shell process.
- The :kbd:`ctrl-r` history search now uses glob syntax (:issue:`10131`).
- The :kbd:`ctrl-r` history search now operates only on the line or command substitution at cursor, making it easier to combine commands from history.
- Abbreviations can now be restricted to specific commands. For instance::
abbr --add --command git back 'reset --hard HEAD^'
will expand "back" to ``reset --hard HEAD^``, but only when the command is ``git`` (:issue:`9411`).
will expand "back" to ``reset --hard HEAD^``, but only when the command is ``git`` (:issue:`9411`, :issue:`10452`).
Deprecations and removed features
---------------------------------
- ``commandline --tokenize`` (short option ``-o``) has been deprecated in favor of ``commandline --tokens-expanded`` (short option ``-x``) which expands variables and other shell syntax, removing the need to use :doc:`eval <cmds/eval>` in completion scripts (:issue:`10212`).
- ``commandline --tokenize`` (short option ``-o``) has been deprecated in favor of ``commandline --tokens-expanded`` (short option ``-x``) which expands variables and other shell syntax, removing the need to use "eval" in completion scripts (:issue:`10212`).
- Two new feature flags:
- ``remove-percent-self`` (see ``status features``) disables PID expansion of ``%self``, which has been supplanted by ``$fish_pid`` (:issue:`10262`).
- ``test-require-arg`` disables ``test``'s one-argument mode. That means ``test -n`` without an additional argument will return false, ``test -z`` will keep returning true. Any other option without an argument, anything that is not an option and no argument will be an error. This also goes for ``[``, test's alternate name.
- ``remove-percent-self`` (see ``status features``) disables PID expansion of ``%self`` which has been supplanted by ``$fish_pid`` (:issue:`10262`).
- ``test-require-arg``, will disable ``test``'s one-argument mode. That means ``test -n`` without an additional argument will return false, ``test -z`` will keep returning true. Any other option without an argument, anything that is not an option and no argument will be an error. This also goes for ``[``, test's alternate name.
This is a frequent source of confusion and so we are breaking with POSIX explicitly in this regard.
In addition to the feature flag, there is a debug category "deprecated-test". Running fish with ``fish -d deprecated-test`` will show warnings whenever a ``test`` invocation that would change is used. (:issue:`10365`).
These can be enabled with::
as always these can be enabled with::
set -Ua fish_features remove-percent-self test-require-arg
We intend to enable them by default in future, and after that eventually make them read-only.
- Specifying key names as terminfo names (using the ``bind -k`` syntax) is deprecated and may be removed in a future version.
They are available as a preview now, it is our intention to enable them by default in future, and after that eventually make them read-only.
- Specifying key names as terminfo name (``bind -k``) is deprecated and may be removed in a future version.
- When a terminal pastes text into fish using bracketed paste, fish used to switch to a special ``paste`` bind mode.
This bind mode has been removed. The behavior on paste is no longer configurable.
- When an interactive fish is stopped or terminated by a signal that cannot be caught (SIGSTOP or SIGKILL), it may leave the terminal in a state where keypresses with modifiers are sent as CSI u sequences, instead of traditional control characters or escape sequences that are recognized by Readline and compatible programs, such as bash and python.
This bind mode has been removed. The behavior on paste is currently not meant to be configurable.
- When an interactive fish is stopped or terminated by a signal that cannot be caught (SIGSTOP or SIGKILL), it may leave the terminal in a state where keypresses with modifiers are sent as CSI u sequences instead of traditional control characters or escape sequences (that are recognized by bash/readline).
If this happens, you can use the ``reset`` command from ``ncurses`` to restore the terminal state.
- ``fish_key_reader --verbose`` no longer shows timing information.
- Terminal information is no longer read from hashed terminfo databases, or termcap databases (:issue:`10269`). The vast majority of systems use a non-hashed terminfo database, which is still supported.
Scripting improvements
----------------------
- ``for`` loops will no longer remember local variables from the previous iteration (:issue:`10525`).
- A new ``history append`` subcommand appends a command to the history, without executing it (:issue:`4506`).
- A new redirection: ``<? /path/to/file`` will try opening the file as input, and if it doesn't succeed silently uses ``/dev/null`` instead.
- for-loops will no longer remember local variables from the previous iteration (:issue:`10525`).
- Add ``history append`` subcommand to append a command to the history without executing it (:issue:`4506`).
- A new redirection: ``<? /path/to/file`` will try opening the file as input, and if it doesn't succeed silently use /dev/null instead.
This can help with checks like ``test -f /path/to/file; and string replace foo bar < /path/to/file``. (:issue:`10387`)
- A new option ``commandline --tokens-raw`` prints a list of tokens without any unescaping (:issue:`10212`).
- A new option ``commandline --showing-suggestion`` tests whether an autosuggestion is currently displayed (:issue:`10586`).
- ``functions`` and ``type`` now show that a function was copied and its source, rather than solely ``Defined interactively`` (:issue:`6575`).
- Stack trace now shows line numbers for copied functions (:issue:`6575`).
- New option ``commandline --tokens-raw`` prints a list of tokens without any unescaping (:issue:`10212`).
- New option ``commandline --showing-suggestion`` to check whether an autosuggestion is currently displayed (:issue:`10586`).
- ``functions`` and ``type`` now show where a function was copied and where it originally was instead of saying ``Defined interactively`` (:issue:`6575`).
- Stack trace now shows line numbers for copied functions.
- ``foo & && bar`` is now a syntax error, like in other shells (:issue:`9911`).
- ``if -e foo; end`` now prints a more accurate error (:issue:`10000`).
- Variables in command position that expand to a subcommand keyword are now forbidden to fix a likely user error.
For example ``set editor command emacs; $editor`` is no longer allowed (:issue:`10249`).
- ``cd`` into a directory that is not readable but accessible (permissions ``--x``) is now possible (:issue:`10432`).
- An integer overflow in ``string repeat`` leading to a near-infinite loop has been fixed (:issue:`9899`).
- ``string shorten`` behaves better in the presence of non-printable characters, including fixing an integer overflow that shortened strings more than intended (:issue:`9854`).
- ``string pad`` no longer allows non-printable characters as padding (:issue:`9854`).
- ``string repeat`` now allows omission of ``-n`` when the first argument is an integer (:issue:`10282`).
- ``string match`` and ``replace`` have a new ``--max-matches`` option to return as soon as the specified number of matches have been identified, which can improve performance in scripts (:issue:`10587`).
- ``string shorten`` behaves better in the presence of non-printable characters, including fixing an integer overflow that shortened strings more than intended. (:issue:`9854`)
- ``string pad`` no longer allows non-printable characters as padding. (:issue:`9854`)
- ``string repeat`` now allows omission of ``-n`` when the first argument is an integer. (:issue:`10282`)
- ``functions --handlers-type caller-exit`` once again lists functions defined as ``function --on-job-exit caller``, rather than them being listed by ``functions --handlers-type process-exit``.
- A new ``set --no-event`` option sets or erases variables without triggering a variable event. This can be useful to change a variable in an event handler (:issue:`10480`).
- Commas in command substitution output are no longer used as separators in brace expansion, preventing a surprising expansion in some cases (:issue:`5048`).
- Universal variables can now store strings containing invalid UTF-8 (:issue:`10313`).
- A new ``path basename -E`` option that causes it to return the basename ("filename" with the directory prefix removed) with the final extension (if any) also removed. This is a shorter version of ``path change-extension "" (path basename $foo)`` (:issue:`10521`).
- A new ``math --scale-mode`` option to select ``truncate``, ``round``, ``floor``, ``ceiling`` as you wish; the default value is ``truncate``. (:issue:`9117`).
- ``random`` is now less strict about its arguments, allowing a start larger or equal to the end. (:issue:`10879`)
- ``set`` has a new ``--no-event`` flag, to set or erase variables without triggering a variable event. This is useful e.g. to change a variable in an event handler. (:issue:`10480`)
- Commas in command substitution output are no longer used as separators in brace expansion, preventing a surprising expansion in rare cases (:issue:`5048`).
- Universal variables can now store strings containing invalid Unicode codepoints (:issue:`10313`).
- ``path basename`` now takes a ``-E`` option that causes it to return the basename (i.e. "filename" with the directory prefix removed) with the final extension (if any) also removed. This is a shorter version of ``path change-extension "" (path basename $foo)`` (:issue:`10521`).
- ``math`` now adds ``--scale-mode`` parameter. You can choose between ``truncate``, ``round``, ``floor``, ``ceiling`` as you wish (default value is ``truncate``). (:issue:`9117`).
Interactive improvements
------------------------
- Autosuggestions were sometimes not shown after recalling a line from history, which has been fixed (:issue:`10287`).
- Up-arrow search matches -- which are highlighted in reverse colors -- are no longer syntax-highlighted, to fix bad contrast with the search match highlighting.
- Command abbreviations (those with ``--position command`` or without a ``--position``) now also expand after decorators like ``command`` (:issue:`10396`).
- Abbreviations now expand after process separators like ``;`` and ``|``. This fixes a regression in version 3.6 (:issue:`9730`).
- When exporting interactively defined functions (using ``type``, ``functions`` or ``funcsave``) the function body is now indented, to match the interactive command line editor (:issue:`8603`).
- :kbd:`ctrl-x` (``fish_clipboard_copy``) on multiline commands now includes indentation (:issue:`10437`).
- :kbd:`ctrl-v` (``fish_clipboard_paste``) now strips ASCII control characters from the pasted text.
This is consistent with normal keyboard input (:issue:`5274`).
- When a command like ``fg %2`` fails to find the given job, it no longer behaves as if no job spec was given (:issue:`9835`).
- Redirection in command position like ``>echo`` is now highlighted as error (:issue:`8877`).
- ``fish_vi_cursor`` now works properly inside the prompt created by builtin ``read`` (:issue:`10088`).
- fish no longer fails to open a FIFO if interrupted by a terminal resize signal (:issue:`10250`).
- ``read --help`` and friends no longer ignore redirections. This fixes a regression in version 3.1 (:issue:`10274`).
- Measuring a command with ``time`` now considers the time taken for command substitution (:issue:`9100`).
- ``fish_add_path`` now automatically enables verbose mode when used interactively (in the command line), in an effort to be clearer about what it does (:issue:`10532`).
- fish no longer adopts TTY modes of failed commands (:issue:`10603`).
- `complete -e cmd` now prevents autoloading completions for `cmd` (:issue:`6716`).
- fish's default color scheme no longer uses the color "blue", as it has bad contrast against the background in a few terminal's default palettes (:issue:`10758`, :issue:`10786`)
The color scheme will not be upgraded for existing installs. If you want, you should select it again via ``fish_config``.
- Command lines which are larger than the terminal are now displayed correctly, instead of multiple blank lines being displayed (:issue:`7296`).
- Prompts that use external commands will no longer produce an infinite loop if the command crashes (:issue:`9796`).
New or improved bindings
^^^^^^^^^^^^^^^^^^^^^^^^
- When the cursor is on a command that resolves to an executable script, :kbd:`alt-o` will now open that script in your editor (:issue:`10266`).
- During up-arrow history search, :kbd:`shift-delete` will delete the current search item and move to the next older item. Previously this was only supported in the history pager.
- :kbd:`shift-delete` will also remove the currently-displayed autosuggestion from history, and remove it as a suggestion.
- :kbd:`ctrl-Z` (also known as :kbd:`ctrl-shift-z`) is now bound to redo.
- :kbd:`alt-delete` now deletes the argument (which may contain quoted spaces) right of the cursor.
- Some improvements to the :kbd:`alt-e` binding which edits the command line in an external editor:
- The editor's cursor position is copied back to fish. This is currently supported for Vim and Kakoune.
- Cursor position synchronization is only supported for a set of known editors, which are now also detected in aliases which use ``complete --wraps``. For example, use ``complete --wraps my-vim vim`` to synchronize cursors when ``EDITOR=my-vim``.
- Multiline commands are indented before being sent to the editor, which matches how they are displayed in fish.
- The ``...-path-component`` bindings, like ``backward-kill-path-component``, now treat ``#`` as part of a path component (:issue:`10271`).
- Bindings like :kbd:`alt-l` that print output in between prompts now work correctly with multiline commandlines.
- :kbd:`ctrl-c` no longer cancels builtin ``read``.
- :kbd:`alt-d` on an empty command line lists the directory history again. This restores the behavior of version 2.1.
- ``history-prefix-search-backward`` and ``-forward`` now maintain the cursor position, instead of moving the cursor to the end of the command line (:issue:`10430`).
- The following keys have refined behavior if the terminal supports :ref:`the new keyboard encodings <changelog-new-bindings>`:
- :kbd:`shift-enter` now inserts a newline instead of executing the command line.
- :kbd:`ctrl-backspace` now deletes the last word instead of only one character (:issue:`10741`).
- :kbd:`ctrl-delete` deletes the next word (same as :kbd:`alt-d`).
- New special input functions:
- ``forward-char-passive`` and ``backward-char-passive`` are like their non-passive variants but do not accept autosuggestions or move focus in the completion pager (:issue:`10398`).
- ``forward-token``, ``backward-token``, ``kill-token``, and ``backward-kill-token`` are similar to the ``*-bigword`` variants but for the whole argument token which includes escaped spaces (:issue:`2014`).
- The ``accept-autosuggestion`` special input function now returns false when there was nothing to accept (:issue:`10608`).
- Vi mode has seen some improvements but continues to suffer from the lack of people working on it.
- New default cursor shapes for insert and replace mode.
- Insert-mode :kbd:`ctrl-n` accepts autosuggestions (:issue:`10339`).
- Outside insert mode, the cursor will no longer be placed beyond the last character on the commandline.
- When the cursor is at the end of the commandline, a single :kbd:`l` will accept an autosuggestion (:issue:`10286`).
- The cursor position after pasting (:kbd:`p`) has been corrected.
- When the cursor is at the start of a line, escaping from insert mode no longer moves the cursor to the previous line.
- Added bindings for clipboard interaction, like :kbd:`",+,p` and :kbd:`",+,y,y`.
- Deleting in visual mode now moves the cursor back, matching vi (:issue:`10394`).
- Support :kbd:`%` motion (:issue:`10593`).
- Support `ab` and `ib` vi text objects. New input functions are introduced ``jump-{to,till}-matching-bracket`` (:issue:`1842`).
- The :kbd:`E` binding now correctly handles the last character of the word, by jumping to the next word (:issue:`9700`).
Completions
^^^^^^^^^^^
- When using :kbd:`ctrl-x` on Wayland in the VSCode terminal, the clipboard is no longer cleared on :kbd:`ctrl-c`.
- Command-specific tab completions may now offer results whose first character is a period. For example, it is now possible to tab-complete ``git add`` for files with leading periods. The default file completions hide these files, unless the token itself has a leading period (:issue:`3707`).
- Option completion now uses fuzzy subsequence filtering, just like non-option completion (:issue:`830`).
This means that ``--fb`` may be completed to ``--foobar`` if there is no better match.
@@ -196,38 +141,106 @@ Completions
If the ``=`` or ``:`` is actually part of the filename, it will be escaped as ``\:`` and ``\=``,
and no longer get this special treatment.
This matches Bash's behavior.
- Autosuggestions were sometimes not shown after recalling a line from history, which has been fixed (:issue:`10287`).
- Up-arrow search matches -- which are highlighted in reverse video -- are no longer syntax-highlighted, to fix bad contrast with the search match highlighting.
- Command abbreviations (those with ``--position command`` or without a ``--position``) now also expand after decorators like ``command`` (:issue:`10396`).
- Abbreviations now expand after process separators like ``;`` and ``|``. This fixes a regression in version 3.6 (:issue:`9730`).
- When exporting interactively defined functions (using ``type``, ``functions`` or ``funcsave``) the function body is now indented, same as in the interactive command line editor (:issue:`8603`).
- :kbd:`ctrl-x` (``fish_clipboard_copy``) on multiline commands now includes indentation (:issue:`10437`).
- :kbd:`ctrl-v` (``fish_clipboard_paste``) now strips ASCII control characters from the pasted text.
This is consistent with normal keyboard input (:issue:`5274`).
- When a command like ``fg %2`` fails to find the given job, it no longer behaves as if no job spec was given (:issue:`9835`).
- Redirection in command position like ``>echo`` is now highlighted as error (:issue:`8877`).
- ``fish_vi_cursor`` now works properly inside the prompt created by builtin ``read`` (:issue:`10088`).
- fish no longer fails to open a fifo if interrupted by a terminal resize signal (:issue:`10250`).
- ``read --help`` and friends no longer ignore redirections. This fixes a regression in version 3.1 (:issue:`10274`).
- Measuring a command with ``time`` now considers the time taken for command substitution (:issue:`9100`).
- ``fish_add_path`` now automatically enables verbose mode when used interactively (in the commandline), in an effort to be clearer about what it does (:issue:`10532`).
- fish no longer adopts TTY modes of failed commands (:issue:`10603`).
- `complete -e cmd` now prevents autoloading completions for `cmd` (:issue:`6716`).
- fish's default colorscheme no longer uses the color "blue" as it has bad contrast against the background in a few terminal's default palettes (:issue:`10758`, :issue:`10786`)
The colorscheme will not be upgraded for existing installs. If you want, you should select it again via ``fish_config``.
New or improved bindings
^^^^^^^^^^^^^^^^^^^^^^^^
- When the cursor is on a command that resolves to an executable script, :kbd:`alt-o` will now open that script in your editor (:issue:`10266`).
- During up-arrow history search, :kbd:`shift-delete` will delete the current search item and move to the next older item. Previously this was only supported in the history pager.
Same for autosuggestions.
- :kbd:`ctrl-Z` (also known as :kbd:`ctrl-shift-z`) is now bound to redo.
- :kbd:`alt-delete` now deletes the argument (which may contain quoted spaces) right of the cursor.
- Some improvements to the :kbd:`alt-e` binding which edits the commandline in an external editor:
- The editor's cursor position is copied back to fish. This is currently supported for Vim and Kakoune.
- Cursor position synchronization is only supported for a set of known editors. This has been extended by also resolving aliases. For example use ``complete --wraps my-vim vim`` to synchronize cursors when ``EDITOR=my-vim``.
- Multiline commands are indented before being sent to the editor, which matches how they are displayed in fish.
- The ``*-path-component`` bindings like ``backward-kill-path-component`` now treat ``#`` as part of a path component (:issue:`10271`).
- Bindings like :kbd:`alt-l` that print output in between prompts now work correctly with multiline commandlines.
- :kbd:`ctrl-c` no longer cancels builtin ``read``.
- :kbd:`alt-d` on an empty command line lists the directory history again. This restores the behavior of version 2.1.
- ``history-prefix-search-{backward,forward}`` now maintain the cursor position instead of moving the cursor to the end of the command line (:issue:`10430`).
- The :kbd:`E` binding in vi mode now correctly handles the last character of the word, by jumping to the next word (:issue:`9700`).
- The following keys have refined behavior if the terminal supports `disambiguating them <https://sw.kovidgoyal.net/kitty/keyboard-protocol/>`_:
- :kbd:`shift-enter` now inserts a newline instead of executing the command line.
- :kbd:`ctrl-backspace` now deletes the last word instead of only one character.
- :kbd:`ctrl-delete` deletes the next word (same as :kbd:`alt-d`).
- New special input functions:
- ``forward-char-passive`` and ``backward-char-passive`` are like their non-passive variants but do not accept autosuggestions or move focus in the completion pager (:issue:`10398`).
- ``forward-token``, ``backward-token``, ``kill-token``, and ``backward-kill-token`` are similar to the ``*-bigword`` variants but for the whole argument token which includes escaped spaces (:issue:`2014`).
- The ``accept-autosuggestion`` special input function now returns false when there was nothing to accept.
- Vi mode has seen some improvements but continues to suffer from the lack of people working on it.
- Insert-mode :kbd:`ctrl-n` accepts autosuggestions (:issue:`10339`).
- Outside insert mode, the cursor will no longer be placed beyond the last character on the commandline.
- When the cursor is at the end of the commandline, a single :kbd:`l` will accept an autosuggestion (:issue:`10286`).
- The cursor position after pasting (:kbd:`p`) has been corrected.
- When the cursor is at the start of a line, escaping from insert mode no longer moves the cursor to the previous line.
- Added bindings for clipboard interaction, like :kbd:`",+,p` and :kbd:`",+,y,y`.
- Deleting in visual mode now moves the cursor back, matching vi (:issue:`10394`).
- Support :kbd:`%` motion (:issue:`10593`).
- Support `ab` and `ib` vi text objects. New input functions are introduced ``jump-{to,till}-matching-bracket`` (:issue:`1842`).
Completions
^^^^^^^^^^^
- Various new completion scripts and numerous updates to existing ones.
- Generated completions are now stored in ``$XDG_CACHE_HOME/fish`` or ``~/.cache/fish`` by default (:issue:`10369`)
Improved terminal support
^^^^^^^^^^^^^^^^^^^^^^^^^
- fish now marks the prompt and command-output regions (via OSC 133) to enable terminal shell integration (:issue:`10352`).
- Fish now marks the prompt and command-output regions (via OSC 133) to enable terminal shell integration (:issue:`10352`).
Shell integration shortcuts can scroll to the next/previous prompt or show the last command output in a pager.
- fish now reports the working directory (via OSC 7) unconditionally instead of only for some terminals (:issue:`9955`).
- fish now sets the terminal window title (via OSC 0) unconditionally instead of only for some terminals (:issue:`10037`).
- Fish now reports the working directory (via OSC 7) unconditionally instead of only for some terminals (:issue:`9955`).
- Fish now sets the terminal window title (via OSC 0) unconditionally instead of only for some terminals (:issue:`10037`).
- Focus reporting in tmux is no longer disabled on the first prompt.
- Focus reporting is now disabled during commands run inside key bindings (:issue:`6942`).
- Cursor changes are applied to all terminals that support them, and the list of specifically-supported terminals has been removed (:issue:`10693`).
- If it cannot find the terminfo entry given by :envvar:`TERM` environment variable, fish will now use an included ``xterm-256color`` definition to match the vast majority of current terminal emulators (:issue:`10905`). If you need to have a specific terminfo profile for your terminal's ``TERM`` variable, install it into the terminfo database.
- Further improvements to the correct display of prompts which fill the width of the terminal (:issue:`8164`).
- Focus reporting is now disabled during execution of bind commands (:issue:`6942`).
- ``fish_vi_cursor`` no longer attempts to detect if the terminal is capable, as we can no longer find terminals that aren't and the allowlist was hard to keep up-to-date. (:issue:`10693`)
Other improvements
------------------
- ``fish_indent`` will now collapse multiple empty lines into one (:issue:`10325`).
- ``fish_indent`` will now collapse multiple successive empty lines into one (:issue:`10325`).
- ``fish_indent`` now preserves the modification time of files if there were no changes (:issue:`10624`).
- Performance and interactivity under Windows Subsystem for Linux has been improved, with a workaround for Windows-specific locations being appended to ``$PATH`` by default (:issue:`10506`).
- Additional filesystems such as AFS are properly detected as remote, which avoids certain hangs due to expensive filesystem locks (:issue:`10818`).
- The HTML-based configuration UI (``fish_config``) now uses Alpine.js instead of AngularJS (:issue:`9554`).
- ``fish_config`` now also works in a Windows MSYS environment (:issue:`10111`).
- Performance and interactivity under WSLv1 and WSLv2 has been improved with a workaround for Windows-specific locations being appended to ``$PATH`` by default (:issue:`10506`).
.. _rust-packaging:
For distributors
----------------
fish has been ported to Rust. This means a significant change in dependencies, which are listed in the README. In short, Rust 1.70 or greater is required, and a C++ compiler is no longer needed (although a C compiler is still required, for some C glue code and the tests).
Fish has been ported to Rust. That means the dependencies have changed.
CMake remains the recommended build system, because of cargo's limited support for installing support files. Version 3.5 remains the minimum supported version. The Xcode generator for CMake is not supported any longer (:issue:`9924`)
It now requires Rust 1.70 at least.
fish no longer depends on the ncurses library, but still uses a terminfo database. When packaging fish, please add a dependency on the package containing your terminfo database instead of curses.
CMake remains for now because cargo is unable to install the many asset files that fish needs. The minimum required CMake version has increased to 3.19.
Some smaller changes:
- The default build configuration has changed to "Debug".
Please pass ``-DCMAKE_BUILD_TYPE=Release`` if you want to build a package.
- Xcode support has been removed (:issue:`9924`).
- fish no longer links against the (n)curses library, opting to read the terminfo database via the terminfo crate.
This means hashed terminfo databases are no longer supported (from our research, they are basically unmaintained and unused).
When packaging fish, please add a dependency on the package containing your terminfo database instead of curses,
if such a package is required.
If it cannot find a terminfo database, fish will now fall back on an included ``xterm-256color`` definition (:issue:`10269`).
--------------

View File

@@ -5,7 +5,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
project(fish LANGUAGES C)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(DEFAULT_BUILD_TYPE "RelWithDebInfo")
set(DEFAULT_BUILD_TYPE "Debug")
# Generate Xcode schemas (but not for tests).
set(CMAKE_XCODE_GENERATE_SCHEME 1)
@@ -58,7 +58,6 @@ function(CREATE_TARGET target)
$<$<CONFIG:Release>:--release>
$<$<CONFIG:RelWithDebInfo>:--release>
--target ${Rust_CARGO_TARGET}
--no-default-features
${CARGO_FLAGS}
${FEATURES_ARG}
&&

View File

@@ -42,8 +42,8 @@ Guidelines
In short:
- Be conservative in what you need (keep to the agreed minimum supported Rust version, limit new dependencies)
- Use automated tools to help you (including ``make test`` and ``build_tools/style.fish``)
- Be conservative in what you need (``C++11``, few dependencies)
- Use automated tools to help you (including ``make test``, ``build_tools/style.fish`` and ``make lint``)
Contributing completions
========================
@@ -101,7 +101,7 @@ To ensure your changes conform to the style rules run
before committing your change. That will run our autoformatters:
- ``rustfmt`` for Rust
- ``git-clang-format`` for c++
- ``fish_indent`` (shipped with fish) for fish script
- ``black`` for python
@@ -322,12 +322,12 @@ Setting Code Up For Translations
--------------------------------
All non-debug messages output for user consumption should be marked for
translation. In Rust, this requires the use of the ``wgettext!`` or ``wgettext_fmt!``
macros:
translation. In C++, this requires the use of the ``_`` (underscore)
macro:
::
streams.out.append(wgettext_fmt!("%ls: There are no jobs\n", argv[0]));
streams.out.append_format(_(L"%ls: There are no jobs\n"), argv[0]);
All messages in fish script must be enclosed in single or double quote
characters for our message extraction script to find them.

160
Cargo.lock generated
View File

@@ -20,15 +20,6 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cc"
version = "1.1.30"
@@ -52,25 +43,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cpufeatures"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "dashmap"
version = "5.5.3"
@@ -84,16 +56,6 @@ dependencies = [
"parking_lot_core",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -107,12 +69,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys",
]
[[package]]
name = "fish"
version = "4.0.0-alpha1"
version = "0.1.0"
dependencies = [
"bitflags",
"cc",
@@ -128,7 +90,6 @@ dependencies = [
"portable-atomic",
"rand",
"rsconf",
"rust-embed",
"serial_test",
"terminfo",
"widestring",
@@ -154,16 +115,6 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@@ -420,49 +371,6 @@ dependencies = [
"cc",
]
[[package]]
name = "rust-embed"
version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn 2.0.79",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
dependencies = [
"sha2",
"walkdir",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -489,18 +397,7 @@ checksum = "079a83df15f85d89a68d64ae1238f142f172b1fa915d0d76b26a7cba1b659a69"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"syn",
]
[[package]]
@@ -532,17 +429,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "terminfo"
version = "0.9.0"
@@ -555,49 +441,18 @@ dependencies = [
"phf_codegen",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "widestring"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
[[package]]
name = "winapi-util"
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"
@@ -607,15 +462,6 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"

View File

@@ -7,12 +7,11 @@ rust-version = "1.70"
edition = "2021"
[profile.release]
overflow-checks = true
lto = true
[package]
name = "fish"
version = "4.0.0-beta.1"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
default-run = "fish"
@@ -53,7 +52,6 @@ rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
widestring = "1.1.0"
# We need 0.9.0 specifically for some crash fixes.
terminfo = "0.9.0"
rust-embed = { version = "8.2.0", optional = true }
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
portable-atomic = { version = "1", default-features = false, features = [
@@ -84,9 +82,8 @@ name = "fish_key_reader"
path = "src/bin/fish_key_reader.rs"
[features]
default = ["installable"]
default = []
benchmark = []
installable = ["dep:rust-embed"]
# The following features are auto-detected by the build-script and should not be enabled manually.
asan = []
@@ -99,4 +96,3 @@ rust.unknown_lints = "allow"
rust.unstable_name_collisions = "allow"
clippy.manual_range_contains = "allow"
clippy.needless_return = "allow"
clippy.needless_lifetimes = "allow"

View File

@@ -66,7 +66,8 @@ Windows
for Linux with the instructions for the appropriate distribution
listed above under “Packages for Linux”, or from source with the
instructions below.
- fish (4.0 on and onwards) cannot be installed in Cygwin, due to a lack of Rust support.
- Fish can also be installed on all versions of Windows using
`Cygwin <https://cygwin.com/>`__ (from the **Shells** category).
Building from source
~~~~~~~~~~~~~~~~~~~~
@@ -120,27 +121,33 @@ Building
Dependencies
~~~~~~~~~~~~
Compiling fish requires:
Compiling fish from a tarball requires:
- a C++11 compiler (g++ 4.8 or later, or clang 3.3 or later)
- CMake (version 3.5 or later)
- PCRE2 (headers and libraries) - optional, this will be downloaded if missing
- gettext (headers and libraries) - optional, for translation support
Sphinx is also optionally required to build the documentation from a
cloned git repository.
Additionally, running the test suite requires Python 3.5+ and the pexpect package.
Dependencies, git master
~~~~~~~~~~~~~~~~~~~~~~~~
Building from git master currently requires:
- Rust (version 1.70 or later)
- CMake (version 3.5 or later)
- CMake (version 3.19 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
- an Internet connection, as other dependencies will be downloaded automatically
Sphinx is also optionally required to build the documentation from a
cloned git repository.
Additionally, running the full test suite requires Python 3, tmux, and the pexpect package.
Building from source with CMake
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rather than building from source, consider using a packaged build for your platform. Using the
steps below makes fish difficult to uninstall or upgrade. Release packages are available from the
links above, and up-to-date `development builds of fish are available for many platforms
<https://github.com/fish-shell/fish-shell/wiki/Development-builds>`__
Building from source (all platforms) - Makefile generator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To install into ``/usr/local``, run:
@@ -148,50 +155,35 @@ To install into ``/usr/local``, run:
mkdir build; cd build
cmake ..
cmake --build .
sudo cmake --install .
make
sudo make install
The install directory can be changed using the
``-DCMAKE_INSTALL_PREFIX`` parameter for ``cmake``.
CMake Build options
~~~~~~~~~~~~~~~~~~~
Build options
~~~~~~~~~~~~~
In addition to the normal CMake build options (like ``CMAKE_INSTALL_PREFIX``), fish's CMake build has some other options available to customize it.
In addition to the normal CMake build options (like ``CMAKE_INSTALL_PREFIX``), fish has some other options available to customize it.
- BUILD_DOCS=ON|OFF - whether to build the documentation. This is automatically set to OFF when Sphinx isn't installed.
- INSTALL_DOCS=ON|OFF - whether to install the docs. This is automatically set to on when BUILD_DOCS is or prebuilt documentation is available (like when building in-tree from a tarball).
- FISH_USE_SYSTEM_PCRE2=ON|OFF - whether to use an installed pcre2. This is normally autodetected.
- MAC_CODESIGN_ID=String|OFF - the codesign ID to use on Mac, or "OFF" to disable codesigning.
- WITH_GETTEXT=ON|OFF - whether to build with gettext support for translations.
- extra_functionsdir, extra_completionsdir and extra_confdir - to compile in an additional directory to be searched for functions, completions and configuration snippets
Building fish as self-installable (experimental)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note that fish does *not* support static linking and will attempt to error out if it detects it.
You can also build fish as a self-installing binary.
Help, it didnt build!
~~~~~~~~~~~~~~~~~~~~~~
This will include all the datafiles like the included functions or web configuration tool in the main ``fish`` binary.
On Debian or Ubuntu you want these packages:
On the first interactive run, and whenever it notices they are out of date, it will extract the datafiles to ~/.local/share/fish/install/ (currently, subject to change). You can do this manually by running ``fish --install``.
::
To install fish as self-installable, just use ``cargo``, like::
sudo apt install build-essential cmake libpcre2-dev gettext
cargo install --path /path/to/fish # if you have a git clone
cargo install --git https://github.com/fish-shell/fish-shell --tag 4.0 # to build from git once 4.0 is released
cargo install --git https://github.com/fish-shell/fish-shell # to build the current development snapshot without cloning
This will place the binaries in ``~/.cargo/bin/``, but you can place them wherever you want.
This build won't have the HTML docs (``help`` will open the online version) or translations.
It will try to build the man pages with sphinx-build. If that is not available and you would like to include man pages, you need to install it and retrigger the build script, e.g. by setting FISH_BUILD_DOCS=1::
FISH_BUILD_DOCS=1 cargo install --path .
Setting it to "0" disables the inclusion of man pages.
You can also link this build statically (but not against glibc) and move it to other computers.
On RedHat, CentOS, or Amazon EC2 everything should be preinstalled.
Contributing Changes to the Code
--------------------------------

139
build.rs
View File

@@ -29,20 +29,13 @@ fn main() {
.unwrap(),
);
let version = &get_version(&env::current_dir().unwrap());
// Per https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script,
// the source directory is the current working directory of the build script
rsconf::set_env_value("FISH_BUILD_VERSION", version);
rsconf::set_env_value(
"FISH_BUILD_VERSION",
&get_version(&env::current_dir().unwrap()),
);
std::env::set_var("FISH_BUILD_VERSION", version);
#[cfg(feature = "installable")]
#[cfg(not(clippy))]
{
let cman = std::fs::canonicalize(env!("CARGO_MANIFEST_DIR")).unwrap();
let targetman = cman.as_path().join("target").join("man");
build_man(&targetman);
}
rsconf::rebuild_if_path_changed("src/libc.c");
cc::Build::new()
.file("src/libc.c")
@@ -230,18 +223,10 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
var
}
let (prefix_from_home, prefix) = if let Ok(pre) = env::var("PREFIX") {
(false, PathBuf::from(pre))
} else {
(true, PathBuf::from(".local/"))
};
// If someone gives us a $PREFIX, we need it to be absolute.
// Otherwise we would try to get it from $HOME and that won't really work.
if !prefix_from_home && prefix.is_relative() {
let prefix = PathBuf::from(env::var("PREFIX").unwrap_or("/usr/local".to_string()));
if prefix.is_relative() {
panic!("Can't have relative prefix");
}
rsconf::rebuild_if_env_changed("PREFIX");
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
@@ -249,24 +234,11 @@ fn get_path(name: &str, default: &str, onvar: PathBuf) -> PathBuf {
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
rsconf::rebuild_if_env_changed("DATADIR");
let datadir_subdir = if prefix_from_home {
"fish/install"
} else {
"fish"
};
rsconf::set_env_value("DATADIR_SUBDIR", datadir_subdir);
let bindir = get_path("BINDIR", "bin/", prefix.clone());
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
rsconf::rebuild_if_env_changed("BINDIR");
let sysconfdir = get_path(
"SYSCONFDIR",
// 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(),
);
let sysconfdir = get_path("SYSCONFDIR", "etc/", datadir.clone());
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
rsconf::rebuild_if_env_changed("SYSCONFDIR");
@@ -298,7 +270,7 @@ fn get_version(src_dir: &Path) -> String {
if !rev.is_empty() {
// If it contains a ".", we have a proper version like "3.7",
// or "23.2.1-1234-gfab1234"
if rev.contains('.') {
if rev.contains(".") {
return rev;
}
// If it doesn't, we probably got *just* the commit SHA,
@@ -311,98 +283,7 @@ fn get_version(src_dir: &Path) -> String {
return version + "-g" + &rev;
}
}
// TODO: Do we just use the cargo version here?
// git did not tell us a SHA either because it isn't installed,
// or because it refused (safe.directory applies to `git describe`!)
// So we read the SHA ourselves.
fn get_git_hash() -> Result<String, Box<dyn std::error::Error>> {
let gitdir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".git");
// .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];
let refstr = refstr.trim();
let version = env!("CARGO_PKG_VERSION").to_owned();
Ok(version + "-g" + refstr)
}
get_git_hash().expect("Could not get a version. Either set $FISH_BUILD_VERSION or install git.")
}
#[cfg(feature = "installable")]
// disable clippy because otherwise it would panic without sphinx
#[cfg(not(clippy))]
fn build_man(build_dir: &Path) {
use std::process::Command;
let mandir = build_dir;
let sec1dir = mandir.join("man1");
let docsrc_path = std::fs::canonicalize(env!("CARGO_MANIFEST_DIR"))
.unwrap()
.as_path()
.join("doc_src");
let docsrc = docsrc_path.to_str().unwrap();
let args = &[
"-j",
"auto",
"-q",
"-b",
"man",
"-c",
docsrc,
// doctree path - put this *above* the man1 dir to exclude it.
// this is ~6M
"-d",
mandir.to_str().unwrap(),
docsrc,
sec1dir.to_str().unwrap(),
];
let _ = std::fs::create_dir_all(sec1dir.to_str().unwrap());
rsconf::rebuild_if_env_changed("FISH_BUILD_DOCS");
if env::var("FISH_BUILD_DOCS") == Ok("0".to_string()) {
println!("cargo:warning=Skipping man pages because $FISH_BUILD_DOCS is set to 0");
return;
}
// We run sphinx to build the man pages.
// Every error here is fatal so cargo doesn't cache the result
// - if we skipped the docs with sphinx not installed, installing it would not then build the docs.
// That means you need to explicitly set $FISH_BUILD_DOCS=0 (`FISH_BUILD_DOCS=0 cargo install --path .`),
// which is unfortunate - but the docs are pretty important because they're also used for --help.
match Command::new("sphinx-build").args(args).spawn() {
Err(x) if x.kind() == std::io::ErrorKind::NotFound => {
if env::var("FISH_BUILD_DOCS") == Ok("1".to_string()) {
panic!("Could not find sphinx-build to build man pages.\nInstall sphinx or disable building the docs by setting $FISH_BUILD_DOCS=0.");
}
println!("cargo:warning=Cannot find sphinx-build to build man pages.");
println!("cargo:warning=If you install it now you need to run `cargo clean` and rebuild, or set $FISH_BUILD_DOCS=1 explicitly.");
}
Err(x) => {
// Another error - permissions wrong etc
panic!("Error starting sphinx-build to build man pages: {:?}", x);
}
Ok(mut x) => match x.wait() {
Err(err) => {
panic!(
"Error waiting for sphinx-build to build man pages: {:?}",
err
);
}
Ok(out) => {
if out.success() {
// Success!
return;
} else {
panic!("sphinx-build failed to build the man pages.");
}
}
},
}
"unknown".to_string()
}

View File

@@ -53,16 +53,16 @@ fi
if test -r "$FBVF"
then
VC=$(cat "$FBVF")
VC=$(grep -v '^#' "$FBVF" | tr -d '"' | sed -e 's/^FISH_BUILD_VERSION=//')
else
VC="unset"
fi
# Maybe output the FBVF
# It looks like "2.7.1-621-ga2f065e6"
# It looks like FISH_BUILD_VERSION="2.7.1-621-ga2f065e6"
test "$VN" = "$VC" || {
echo >&2 "$VN"
echo "$VN" >"$FBVF"
echo >&2 "FISH_BUILD_VERSION=$VN"
echo "FISH_BUILD_VERSION=\"$VN\"" >"$FBVF"
}
# Output the fish-build-version-witness.txt

View File

@@ -142,7 +142,7 @@ fi
(cd "$PKGDIR/build_arm64" && env $ARM64_DEPLOY_TARGET make -j 12 fish_macapp)
(cd "$PKGDIR/build_x86_64" && env $X86_64_DEPLOY_TARGET make -j 12 fish_macapp)
# Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtesy of CMake.
# Make the app's /usr/local/bin binaries universal. Note fish.app/Contents/MacOS/fish already is, courtsey of CMake.
cd "$PKGDIR/build_arm64"
for FILE in fish.app/Contents/Resources/base/usr/local/bin/*; do
X86_FILE="$PKGDIR/build_x86_64/fish.app/Contents/Resources/base/usr/local/bin/$(basename $FILE)"

View File

@@ -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 PATH="${CMAKE_BINARY_DIR}:$$PATH"
${SPHINX_EXECUTABLE}
-j auto
-q -b man
@@ -38,7 +38,7 @@ add_custom_target(sphinx-manpages
"${SPHINX_SRC_DIR}"
# TODO: This only works if we only have section 1 manpages.
"${SPHINX_MANPAGE_DIR}/man1"
DEPENDS CHECK-FISH-BUILD-VERSION-FILE
DEPENDS fish_indent
COMMENT "Building man pages with Sphinx")
if(SPHINX_EXECUTABLE)

View File

@@ -265,6 +265,18 @@ else()
endif()
if (_RUSTC_VERSION_RAW MATCHES "rustup [0-9\\.]+")
if (_USER_SPECIFIED_RUSTC)
message(
WARNING "User-specified Rust_COMPILER pointed to rustup's rustc proxy. Corrosion's "
"FindRust will always try to evaluate to an actual Rust toolchain, and so the "
"user-specified Rust_COMPILER will be discarded in favor of the default "
"rustup-managed toolchain."
)
unset(Rust_COMPILER)
unset(Rust_COMPILER CACHE)
endif()
# Get `rustup` next to the `rustc` proxy
get_filename_component(_RUST_PROXIES_PATH "${_Rust_COMPILER_TEST}" DIRECTORY)
find_program(Rust_RUSTUP rustup HINTS "${_RUST_PROXIES_PATH}" NO_DEFAULT_PATH)

View File

@@ -106,7 +106,7 @@ configure_file(fish.pc.in fish.pc.noversion @ONLY)
add_custom_command(OUTPUT fish.pc
COMMAND sed '/Version/d' fish.pc.noversion > fish.pc
COMMAND printf "Version: " >> fish.pc
COMMAND cat ${FBVF} >> fish.pc
COMMAND sed 's/FISH_BUILD_VERSION=//\;s/\"//g' ${FBVF} >> fish.pc
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS CHECK-FISH-BUILD-VERSION-FILE ${CMAKE_CURRENT_BINARY_DIR}/fish.pc.noversion)

View File

@@ -54,7 +54,6 @@ set(TEST_ROOT_DIR ${TEST_DIR}/root)
if(NOT FISH_IN_TREE_BUILD)
add_custom_target(funcs_dir)
add_custom_command(TARGET funcs_dir
POST_BUILD
COMMAND mkdir -p ${CMAKE_BINARY_DIR}/share
# Don't run ln twice or it will create a new link in the link.
COMMAND test -e ${CMAKE_BINARY_DIR}/share/functions || ln -sf
@@ -64,7 +63,6 @@ if(NOT FISH_IN_TREE_BUILD)
add_custom_target(tests_dir DEPENDS tests)
add_custom_command(TARGET tests_dir
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/tests/ ${CMAKE_BINARY_DIR}/tests/
COMMENT "Copying test files to binary dir"
@@ -155,7 +153,7 @@ endif()
add_test(
NAME "cargo-test"
COMMAND env ${VARS_FOR_CARGO} cargo test --no-default-features ${CARGO_FLAGS} --workspace --target-dir ${rust_target_dir} ${cargo_test_flags}
COMMAND env ${VARS_FOR_CARGO} cargo test ${CARGO_FLAGS} --workspace --target-dir ${rust_target_dir} ${cargo_test_flags}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)
set_tests_properties("cargo-test" PROPERTIES SKIP_RETURN_CODE ${SKIP_RETURN_CODE})

6
debian/control vendored
View File

@@ -6,7 +6,7 @@ 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,
# Test dependencies
locales-all, ncurses-base, python3
locales-all, python3
Standards-Version: 4.1.5
Homepage: https://fishshell.com/
Vcs-Git: https://github.com/fish-shell/fish-shell.git
@@ -14,8 +14,8 @@ Vcs-Browser: https://github.com/fish-shell/fish-shell
Package: fish
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, gettext-base, man-db, ncurses-base, procps,
python3 (>=3.5)
Depends: ${shlibs:Depends}, ${misc:Depends}, passwd (>= 4.0.3-10), gettext-base, man-db,
procps, python3 (>=3.5)
Conflicts: fish-common
Recommends: xsel (>=1.2.0)
Suggests: xdg-utils

View File

@@ -1,22 +0,0 @@
[licenses]
# We want really high confidence when inferring licenses from text
confidence-threshold = 0.93
unused-allowed-license = "allow" # don't warn for unused licenses in this list
allow = [
"BSD-2-Clause",
"BSD-3-Clause",
"BSL-1.0",
"GPL-2.0",
"ISC",
"LGPL-2.0",
"MIT",
"MPL-2.0",
"PSF-2.0",
"Unicode-DFS-2016",
"WTFPL",
"Zlib",
]
[sources.allow-org]
# 1 or more github.com organizations to allow git sources for
github = ["fish-shell"]

View File

@@ -23,7 +23,7 @@ If both ``KEYS`` and ``COMMAND`` are given, ``bind`` adds (or replaces) a bindin
If only ``KEYS`` is given, any existing binding in the given ``MODE`` will be printed.
``KEYS`` is a comma-separated list of key names.
Modifier keys can be specified by prefixing a key name with a combination of ``ctrl-``, ``alt-`` and ``shift-``.
Modifier keys can be specified by prefixing a key name with a combination of ``ctrl-``/``c-``, ``alt-``/``a-`` and ``shift-``.
For example, pressing :kbd:`w` while holding the Alt modifier is written as ``alt-w``.
Key names are case-sensitive; for example ``alt-W`` is the same as ``alt-shift-w``.
``ctrl-x,ctrl-e`` would mean pressing :kbd:`ctrl-x` followed by :kbd:`ctrl-e`.

View File

@@ -97,12 +97,7 @@ If ``commandline`` is called during a call to complete a given string using ``co
The following options output metadata about the commandline state:
**-L** or **--line**
If no argument is given, print the line that the cursor is on, with the topmost line starting at 1.
Otherwise, set the cursor to the given line.
**--column**
If no argument is given, print the 1-based offset from the start of the line to the cursor position in Unicode code points.
Otherwise, set the cursor to the given code point offset.
Print the line that the cursor is on, with the topmost line starting at 1.
**-S** or **--search-mode**
Evaluates to true if the commandline is performing a history search.

View File

@@ -1,50 +0,0 @@
.. _cmd-export:
export - compatibility function for exporting variables
=======================================================
Synopsis
--------
.. synopsis::
export
export NAME=VALUE
Description
-----------
``export`` is a function included for compatibility with POSIX shells. In general, the :doc:`set <set>`
builtin should be used instead.
When called without arguments, ``export`` prints a list of currently-exported variables, like ``set
-x``.
When called with a ``NAME=VALUE`` pair, the variable ``NAME`` is set to ``VALUE`` in the global
scope, and exported as an environment variable to other commands.
There are no options available.
Example
-------
The following commands have an identical effect.
::
set -gx PAGER bat
export PAGER=bat
Note: If you want to add to e.g. ``$PATH``, you need to be careful to :ref:`combine the list <cartesian-product>`. Quote it, like so::
export PATH="$PATH:/opt/bin"
Or just use ``set``, which avoids this::
set -gx PATH $PATH /opt/bin
See more
--------
1. The :doc:`set <set>` command.

View File

@@ -40,10 +40,6 @@ The following options are available:
**-i** or **--interactive**
The shell is interactive.
**--install**
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.
**-l** or **--login**
Act as if invoked as a login shell.

View File

@@ -279,7 +279,7 @@ Examples
path normalize [-z | --null-in] [-Z | --null-out] [-q | --quiet] [PATH ...]
``path normalize`` returns the normalized versions of all paths. That means it squashes duplicate "/", collapses "../" with earlier components and removes "." components.
``path normalize`` returns the normalized versions of all paths. That means it squashes duplicate "/" (except for two leading "//"), collapses "../" with earlier components and removes "." components.
Unlike ``realpath`` or ``path resolve``, it does not make the paths absolute. It also does not resolve any symlinks. As such it can operate on non-existent paths.

View File

@@ -18,11 +18,7 @@ Description
.. BEGIN DESCRIPTION
``string trim`` removes leading and trailing whitespace from each *STRING*. If **-l** or **--left** is given, only leading whitespace is removed. If **-r** or **--right** is given, only trailing whitespace is trimmed.
The **-c** or **--chars** switch causes the set of characters in *CHARS* to be removed instead of whitespace. This is a set of characters, not a string - if you pass ``-c foo``, it will remove any "f" or "o", not just "foo" as a whole.
Exit status: 0 if at least one character was trimmed, or 1 otherwise.
``string trim`` removes leading and trailing whitespace from each *STRING*. If **-l** or **--left** is given, only leading whitespace is removed. If **-r** or **--right** is given, only trailing whitespace is trimmed. The **-c** or **--chars** switch causes the characters in *CHARS* to be removed instead of whitespace. Exit status: 0 if at least one character was trimmed, or 1 otherwise.
.. END DESCRIPTION

View File

@@ -79,7 +79,6 @@ Some helper functions, often to give you information for use in your prompt:
- :doc:`fish_add_path <cmds/fish_add_path>` to easily add a path to $PATH.
- :doc:`alias <cmds/alias>` to quickly define wrapper functions ("aliases").
- :doc:`fish_delta <cmds/fish_delta>` to show what you have changed from the default configuration.
- :doc:`export <cmds/export>` as a compatibility function for other shells.
Helper commands
^^^^^^^^^^^^^^^

View File

@@ -65,16 +65,9 @@ issue_url = "https://github.com/fish-shell/fish-shell/issues"
# Parsing FISH-BUILD-VERSION-FILE is possible but hard to ensure that it is in the right place
# fish_indent is guaranteed to be on PATH for the Pygments highlighter anyway
if "FISH_BUILD_VERSION_FILE" in os.environ:
f = open(os.environ["FISH_BUILD_VERSION_FILE"], "r")
ret = f.readline().strip()
elif "FISH_BUILD_VERSION" in os.environ:
ret = os.environ["FISH_BUILD_VERSION"]
else:
ret = subprocess.check_output(
("fish_indent", "--version"), stderr=subprocess.STDOUT
).decode("utf-8")
ret = subprocess.check_output(
("fish_indent", "--version"), stderr=subprocess.STDOUT
).decode("utf-8")
# The full version, including alpha/beta/rc tags
release = ret.strip().split(" ")[-1]
# The short X.Y version

View File

@@ -154,7 +154,7 @@ in :ref:`config.fish <configuration>`.
How do I run a command from history?
------------------------------------
Type some part of the command, and then hit the :kbd:`up` (````) or :kbd:`down` (````) arrow keys to navigate through history matches, or press :kbd:`ctrl-r` to open the history in a searchable pager. In this pager you can press :kbd:`ctrl-r` or :kbd:`ctrl-s` to move to older or younger history respectively.
Type some part of the command, and then hit the :kbd:`up` (````) or :kbd:`ctrl-down` (````) arrow keys to navigate through history matches, or press :kbd:`ctrl-r` to open the history in a searchable pager. In this pager you can press :kbd:`ctrl-r` or :kbd:`ctrl-s` to move to older or younger history respectively.
Additional default key bindings include :kbd:`ctrl-p` (up) and :kbd:`ctrl-n` (down). See :ref:`Searchable command history <history-search>` for more information.
@@ -168,7 +168,7 @@ In general, fish's history recall works like this:
- Like other shells, the Up arrow, ``up`` recalls whole lines, starting from the last executed line. So instead of typing ``!!``, you would just hit the up-arrow.
- If the line you want is far back in the history, type any part of the line and then press Up one or more times. This will filter the recalled lines to ones that include this text, and you will get to the line you want much faster. This replaces "!vi", "!?bar.c" and the like. If you want to see more context, you can press ``ctrl-r`` to open the history in the pager.
- If the line you want is far back in the history, type any part of the line and then press Up one or more times. This will filter the recalled lines to ones that include this text, and you will get to the line you want much faster. This replaces "!vi", "!?bar.c" and the like. If you want to see more context, you can press ``ctlr-b`` to open the history in the pager.
- ``alt-up`` recalls individual arguments, starting from the last argument in the last executed line. This can be used instead of "!$".

View File

@@ -266,9 +266,8 @@ Consider this helper function::
Now let's see a few cases::
# Redirect both stderr and stdout to less
# (can also be spelt as `&|`)
print 2>&1 | less
# or
print &| less
# Show the "out" on stderr, silence the "err"
print >&2 2>/dev/null

View File

@@ -33,8 +33,6 @@ BuildRequires: python3 procps
Requires: python3
Requires: man
Requires: ncurses-base
Requires: procps
# Although the build scripts mangle the version number to be RPM compatible
# for continuous builds (transforming the output of `git describe`), Fedora 32+

View File

@@ -64,16 +64,6 @@ function __fish_adb_list_uninstallable_packages
__fish_adb_run_command pm list packages -3 | string replace 'package:' ''
end
function __fish_adb_list_local_files
set -l token (commandline -ct)*
# Unquoted $token to expand the array
# Return list of directories suffixed with '/'
find $token -maxdepth 0 -type d 2>/dev/null | string replace -r '$' /
# Return list of files
find $token -maxdepth 0 -type f -o -type l 2>/dev/null
end
function __fish_adb_list_files
set -l token (commandline -ct)
@@ -197,8 +187,7 @@ complete -n '__fish_seen_subcommand_from reconnect' -c adb -x -a device -d 'Kick
# commands that accept listing device files
complete -n '__fish_seen_subcommand_from shell' -c adb -f -a "(__fish_adb_list_files)" -d 'File on device'
complete -n '__fish_seen_subcommand_from pull' -c adb -F -a "(__fish_adb_list_files)" -d 'File on device'
complete -n '__fish_seen_subcommand_from push' -c adb -ka "(__fish_adb_list_files)" -d 'File on device'
complete -n '__fish_seen_subcommand_from push' -c adb -ka "(__fish_adb_list_local_files)"
complete -n '__fish_seen_subcommand_from push' -c adb -F -a "(__fish_adb_list_files)" -d 'File on device'
# logcat
complete -n '__fish_seen_subcommand_from logcat' -c adb -f

View File

@@ -2,7 +2,7 @@
function __fish_apt_no_subcommand -d 'Test if apt has yet to be given the subcommand'
for i in (commandline -xpc)
if contains -- $i auto manual minimize-manual hold unhold showauto showmanual showhold
if contains -- $i auto manual hold unhold showauto showmanual showhold
return 1
end
end
@@ -23,7 +23,6 @@ complete -c apt-mark -n __fish_apt_use_package -a '(__fish_print_apt_packages)'
complete -c apt-mark -s h -l help -d 'Display help and exit'
complete -f -n __fish_apt_no_subcommand -c apt-mark -a auto -d 'Mark a package as automatically installed'
complete -f -n __fish_apt_no_subcommand -c apt-mark -a manual -d 'Mark a package as manually installed'
complete -f -n __fish_apt_no_subcommand -c apt-mark -a minimize-manual -d 'Mark all dependencies of meta packages as auto'
complete -f -n __fish_apt_no_subcommand -c apt-mark -a hold -d 'Hold a package, prevent automatic installation or removal'
complete -f -n __fish_apt_no_subcommand -c apt-mark -a unhold -d 'Cancel a hold on a package'
complete -f -n __fish_apt_no_subcommand -c apt-mark -a showauto -d 'Show automatically installed packages'
@@ -32,6 +31,4 @@ complete -f -n __fish_apt_no_subcommand -c apt-mark -a showhold -d 'Show held pa
complete -c apt-mark -s v -l version -d 'Display version and exit'
complete -r -c apt-mark -s c -l config-file -d 'Specify a config file'
complete -r -c apt-mark -s o -l option -d 'Set a config option'
complete -c apt-mark -l color -d 'Turn colors on'
complete -c apt-mark -l no-color -d 'Turn colors off'
complete -r -c apt-mark -s f -l file -d 'Write package statistics to a file'

View File

@@ -737,7 +737,6 @@ complete -c cargo -n "__fish_seen_subcommand_from tree" -s v -l verbose -d 'Use
complete -c cargo -n "__fish_seen_subcommand_from tree" -l frozen -d 'Require Cargo.lock and cache are up to date'
complete -c cargo -n "__fish_seen_subcommand_from tree" -l locked -d 'Require Cargo.lock is up to date'
complete -c cargo -n "__fish_seen_subcommand_from tree" -l offline -d 'Run without accessing the network'
complete -c cargo -n "__fish_seen_subcommand_from uninstall" -fa '(cargo install --list | string replace -rf "(\S+) (.*):" \'$1\t$2\')'
complete -c cargo -n "__fish_seen_subcommand_from uninstall" -s p -l package -d 'Package to uninstall'
complete -c cargo -n "__fish_seen_subcommand_from uninstall" -l bin -d 'Only uninstall the binary NAME'
complete -c cargo -n "__fish_seen_subcommand_from uninstall" -l root -d 'Directory to uninstall packages from'

View File

@@ -1,4 +1,37 @@
complete -c cmd -f -a '(__fish_cmd__complete_args)'
function __cmd_complete_args -d 'Function to generate args'
set -l current_token (commandline -tc)
switch $current_token
case '/t:*'
echo -e '0\tBlack
1\tBlue
2\tGreen
3\tAqua
4\tRed
5\tPurple
6\tYellow
7\tWhite
8\tGray
9\tLight blue
A\tLight green
B\tLight aqua
C\tLight red
D\tLight purple
E\tLight yellow
F\tBright white' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
case '/e:*'
echo -e 'on\tEnable command extensions
off\tDisable command extensions' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
case '/f:*'
echo -e 'on\tEnable file and directory name completion
off\tDisable file and directory name completion' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
case '/v:*'
echo -e 'on\tEnable delayed environment variable expansion
off\tDisable delayed environment variable expansion' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
end
end
complete -c cmd -f -a '(__cmd_complete_args)'
complete -c cmd -f -n 'not __fish_seen_argument -w c -w k' -a /c \
-d 'Carry out the command specified by string and then stop'

View File

@@ -1,3 +1 @@
# Older versions of delta do not have a --generate-completion option and will complain
# to stderr but emit nothing to stdout, making it safe (but a no-op) to source.
delta --generate-completion fish 2>/dev/null | source
delta --generate-completion fish | source

View File

@@ -14,6 +14,11 @@ function __fish_diskutil_mounted_volumes
printf '%s\n' $mountpoints
end
function __fish_diskutil_writeable_volumes
set -l mountpoints (path filter -w /Volumes/*)
printf '%s\n' $mountpoints
end
function __fish_diskutil_using_not_subcommand
not __fish_seen_subcommand_from apfs
and not __fish_seen_subcommand_from appleRAID
@@ -53,7 +58,7 @@ complete -f -c diskutil -n '__fish_diskutil_using_not_subcommand umountDisk' -a
# eject
complete -f -c diskutil -n __fish_use_subcommand -a eject -d 'Eject a volume or disk'
complete -f -c diskutil -n '__fish_diskutil_using_not_subcommand eject' -a '(__fish_diskutil_volumes ; __fish_diskutil_devices)'
complete -f -c diskutil -n '__fish_diskutil_using_not_subcommand eject' -a '(__fish_diskutil_writeable_volumes ; __fish_diskutil_devices)'
# mount
complete -f -c diskutil -n __fish_use_subcommand -a mount -d 'Mount a single volume'

View File

@@ -1,55 +0,0 @@
function __fish_firejail_profiles
path basename -- /etc/firejail/*.profile ~/.config/firejail/*.profile
end
function __fish_firejail_complete_sandboxes
firejail --list | string replace -rf -- '([^:]+):([^:]+):([^:]+):(.*)' '$1\t$3: $4 ($2)\n$3\t$1: $4 ($2)'
end
complete -c firejail -f
complete -c firejail -l help -d 'Show help and exit'
complete -c firejail -l version -d 'Show version and exit'
complete -c firejail -l debug -d 'Print debug info'
complete -c firejail -l debug-blacklists -d 'Debug blacklisting'
complete -c firejail -l debug-whitelists -d 'Debug whitelisting'
complete -c firejail -l debug-caps -d 'Print known capabilities and exit'
complete -c firejail -l debug-errnos -d 'Print known error numbers and exit'
complete -c firejail -l debug-private-lib -d 'Debug --private-lib'
complete -c firejail -l debug-protocols -d 'Print known protocols and exit'
complete -c firejail -l debug-syscalls -d 'Print known syscalls and exit'
complete -c firejail -l debug-syscalls32 -d 'Print known 32-bit syscalls and exit'
complete -c firejail -l list -d 'List all sandboxes'
complete -c firejail -l tree -d 'Print a tree of all sandboxes'
complete -c firejail -l top -d 'Monitor sandboxes (like `top`)'
complete -c firejail -l shutdown -d 'Show help and exit'
# Profiles
# Note: firejail's option parsing is weird and *requires* the --foo=bar form
# So we don't use -r
complete -c firejail -l profiles -d 'Load a custom security profile' -a '(__fish_firejail_profiles)'
# Sandboxes
complete -c firejail -l join -d 'Join existing sandbox' -a '(__fish_firejail_complete_sandboxes)'
# Files
complete -c firejail -l hosts-file -d 'Show help and exit' -a '(__fish_firejail_profiles)'
# directory
complete -c firejail -l chroot -d 'Chroot into this directory' -a '(__fish_complete_directories)'
complete -c firejail -l tmpfs -d 'Mount tmpfs into sandbox' -a '(__fish_complete_directories)'
complete -c firejail -l blacklist -d 'Blacklist dir or file' -F
complete -c firejail -l noblacklist -d 'Disable blacklist for dir or file' -F
complete -c firejail -l whitelist -d 'Whitelist dir or file' -F
complete -c firejail -l nowhitelist -d 'Disable whitelist for dir or file' -F
complete -c firejail -l read-only -d 'Set dir or file read-only' -F
complete -c firejail -l read-write -d 'Set dir or file read-write' -F
# TODO: Comma-separated ("/etc/foo,/usr/etc/foo" bind-mounts /etc/foo on /usr/etc/foo)
complete -c firejail -l bind -d 'Bind-mount file on top of another' -F
complete -c firejail -l private -d 'Mount temporary home directories and delete on exit' -F
complete -c firejail -l netfilter -d 'Enable firewall given by this file' -F
# Interfaces
complete -c firejail -l net -d 'Enable network namespace connected to this interface' -a '(__fish_print_interfaces)'
# Commands
complete -c firejail -a '(__fish_complete_subcommand)'

View File

@@ -1,6 +1,6 @@
# gh, at least as of version 1.17.5, does not write errors to stderr, causing
# `checks/completions.fish` to fail if the `gh-completion` module is missing.
# It also does not exit with a non-zero error code, making this harder than it needs to be :(
set completion "$(gh completion --shell fish 2>/dev/null)"
set completion "$(gh completion --shell fish)"
string match -rq '^Error' -- $completion && return -1
echo $completion | source

View File

@@ -1440,7 +1440,7 @@ complete -c git -n '__fish_git_using_command diff' -s 1 -l base -d 'Compare the
complete -c git -n '__fish_git_using_command diff' -s 2 -l ours -d 'Compare the working tree with the "our branch"'
complete -c git -n '__fish_git_using_command diff' -s 3 -l theirs -d 'Compare the working tree with the "their branch"'
complete -c git -n '__fish_git_using_command diff' -s 0 -d 'Omit diff output for unmerged entries and just show "Unmerged"'
complete -c git -n '__fish_git_using_command diff' -k -n 'not __fish_git_contains_opt cached staged' -a '(
complete -c git -n '__fish_git_using_command diff' -n 'not __fish_git_contains_opt cached staged' -a '(
set -l kinds modified
contains -- -- (commandline -xpc) && set -a kinds deleted modified-staged-deleted
__fish_git_files $kinds

View File

@@ -1,68 +0,0 @@
function __fish_guild__complete_warnings
guild compile -Whelp |
string replace --filter --regex '^\s+`([a-z\-]+)\'\s+(.+)' '$1\t$2'
printf '%s\n' 0 2 3
echo 1\tdefault
end
function __fish_guild__complete_optimizations
guild compile -Ohelp |
string replace --filter --regex '^\s+-O(.+)' '$1\nno-$1'
printf '%s\n' 0 1 3
echo 2\tdefault
end
set -l command guild
complete -c $command -f
set -l compile_condition '__fish_seen_subcommand_from compile'
complete -c $command -a 'compile\tCompile scripts' -n "not $compile_condition"
complete -c $command -s h -l help -d 'Show help' -n $compile_condition
complete -c $command -l version -d 'Show version' -n $compile_condition
complete -c $command -s L -l load-path -F \
-d 'Specify the directory to prepend to module load path' \
-n $compile_condition
complete -c $command -s o -l output -F \
-d 'Specify the output file to put bytecode in' \
-n $compile_condition
complete -c $command -s x -x \
-d 'Specify the extension to prepend to extension list' \
-n $compile_condition
complete -c $command -s W -l warning \
-a '(__fish_complete_list , __fish_guild__complete_warnings)' \
-d 'Specify the warning level for a compilation' \
-n $compile_condition
complete -c $command -s O -l optimize \
-a '(__fish_guild__complete_optimizations)' \
-d 'Specify the optimization level for a compilation' \
-n $compile_condition
for standard in 6 7
set -l option r$standard"rc"
complete -c $command -l $option \
-d "Use $(string upper -- $option) compatible mode" \
-n $compile_condition
end
complete -c $command -s f -l from \
-a 'scheme\tdefault elisp ecmascript' \
-d 'Specify the language for sources' \
-n $compile_condition
complete -c $command -s t -l to \
-a 'rtl\tdefault' \
-d 'Specify the language for an output' \
-n $compile_condition
complete -c $command -s T -l target \
-d 'Specify the target for a code' \
-n $compile_condition

View File

@@ -1,115 +0,0 @@
function __fish_guile__complete_srfis
printf '%s\n' 0\t'cond-expand' \
1\t'List library' \
2\t'and-let*' \
4\t'Homogeneous numeric vector datatypes' \
6\t'Basic String Ports' \
8\t'receive' \
9\t'define-record-type' \
10\t'Hash-Comma Reader Extension' \
11\t'let-values' \
13\t'String Library' \
14\t'Character-set Library' \
16\t'case-lambda' \
17\t'Generalized set!' \
18\t'Multithreading support' \
19\t'Time/Date Library' \
23\t'Error Reporting' \
26\t'specializing parameters' \
27\t'Sources of Random Bits' \
28\t'Basic Format Strings' \
30\t'Nested Multi-line Comments' \
31\t'A special form rec for recursive evaluation' \
34\t'Exception handling for programs' \
35\t'Conditions' \
37\t'args-fold' \
38\t'External Representation for Data With Shared Structure' \
39\t'Parameters' \
41\t'Streams' \
42\t'Eager Comprehensions' \
43\t'Vector Library' \
45\t'Primitives for Expressing Iterative Lazy Algorithms' \
46\t'Basic syntax-rules Extensions' \
55\t'Requiring Features' \
60\t'Integers as Bits' \
61\t'A more general cond clause' \
62\t'S-expression comments' \
64\t'A Scheme API for test suites' \
67\t'Compare procedures' \
69\t'Basic hash tables' \
71\t'Extended let-syntax for multiple values' \
87\t'in case clauses' \
88\t'Keyword Objects' \
98\t'Accessing environment variables' \
105\t'Curly-infix expressions' \
111\t'Boxes' \
119\t'Wisp: simpler indentation-sensitive Scheme'
end
function __fish_guile__complete_function_names
set -l path (commandline -poc |
string match --regex '.*\\.scm$' |
sed -n 1p)
test -e "$path" && begin
cat $path |
string match --all --groups-only --regex '\(\s*define\s+\(\s*(\w+)'
end
end
set -l command guile
complete -c $command -f
complete -c $command -s h -l help -d 'Show help'
complete -c $command -s v -l version -d 'Show version'
complete -c $command -s s -F -r -d 'Specify the script to run'
complete -c $command -s c -x -d 'Specify the code to run'
complete -c $command -s L -F -r \
-d 'Specify the directory to prepend to module load path'
complete -c $command -s C -F -r \
-d 'Specify the directory to prepend to module load path for compiled files'
complete -c $command -s x -x \
-a '.scm\tdefault' \
-d 'Specify the extension to prepend to extension list'
complete -c $command -s l -F -r -d 'Specify the script to load'
complete -c $command -s e -x \
-a '(__fish_guile__complete_function_names)' \
-d 'Specify the entry point of a script'
complete -c $command -o ds \
-d 'Treat the last -s option as if it occurred at this point'
complete -c $command -l use-srfi \
-a '(__fish_complete_list , __fish_guile__complete_srfis)' \
-d 'Specify the SRFI modules to load'
for standard in 6 7
set -l option r$standard"rc"
complete -c $command -l $option \
-d "Use $(string upper -- $option) compatible mode"
end
complete -c $command -l debug -d 'Use debug mode'
complete -c $command -l no-debug -d "Don't use debug mode"
complete -c $command -s q -d "Don't load .guile file"
complete -c $command -l listen \
-a '37146\tdefault' \
-d 'Specify the port to list to'
complete -c $command -l auto-compile -d 'Compile scripts automatically'
complete -c $command -l fresh-auto-compile \
-d 'Compile scripts automatically forcefully'
complete -c $command -l no-auto-compile -d "Don't compile scripts automatically"
complete -c $command -l language \
-a 'scheme\tdefault elisp ecmascript' \
-d 'Specify the language for sources'

View File

@@ -1,29 +0,0 @@
function __fish_iftop_sort_orders
echo -e "2s\tSort by 2s traffic average"
echo -e "10s\tSort by 10s traffic average (default)"
echo -e "40s\tSort by 40s traffic average"
echo -e "source\tSort by source address"
echo -e "destination\tSort by destination address"
end
complete -c iftop -f
complete -c iftop -s h -d "Print a summary of usage"
complete -c iftop -s n -d "Don't do hostname lookups"
complete -c iftop -s N -d "Don't resolve port number to service names"
complete -c iftop -s p -d "Run in promiscuous mode"
complete -c iftop -s P -d "Turn on port display"
complete -c iftop -s l -d "Include link-local IPv6 addresses"
complete -c iftop -s b -d "Don't display bar graphs of traffic"
complete -c iftop -s m -x -d "Set the upper limit for the bandwidth scale"
complete -c iftop -s B -d "Show bandwidth rates in bytes/s rather than bits/s"
complete -c iftop -s i -xa "(__fish_print_interfaces)" -d "Listen to packets on interface"
complete -c iftop -s f -x -d "Use filter code to select the packets to count"
complete -c iftop -s F -x -d "Filter to only specified IPv4 network"
complete -c iftop -s G -x -d "Filter to only specified IPv6 network"
complete -c iftop -s c -r -d "Use specified config file"
complete -c iftop -s o -xa "(__fish_iftop_sort_orders)" -d "Sort by specified column"
complete -c iftop -s t -d "Use text interface without ncurses"
complete -c iftop -s L -x -d "Number of lines to print with -t"
complete -c iftop -s s -x -d "With -t, print single output after specific number of seconds"

View File

@@ -9,7 +9,7 @@ complete -c kak -o p -x -a '(command kak -l)' -d 'just send stdin as commands to
complete -c kak -o f -x -d 'filter: for each file, select the entire buffer and execute the given keys'
complete -c kak -o i -x -d 'backup the files on which a filter is applied using the given suffix'
complete -c kak -o q -d 'in filter mode be quiet about errors applying keys'
complete -c kak -o ui -x -a 'terminal dummy json' -d 'set the type of user interface to use'
complete -c kak -o ui -x -a 'ncurses dummy json' -d 'set the type of user interface to use'
complete -c kak -o l -d 'list existing sessions'
complete -c kak -o clear -d 'clear dead sessions'
complete -c kak -o debug -x -d 'initial debug option value' -a 'hooks shell profile keys commands'

View File

@@ -1 +0,0 @@
kops completion fish 2>/dev/null | source

View File

@@ -1,50 +0,0 @@
# Basic completions for simonw/llm
# A complete implementation for `llm [prompt]` but other subcommands
# can be further fleshed out.
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
printf "%s\t%s\n" "prompt" "Execute a prompt" \
"aliases" "Manage model aliases" \
"chat" "Hold chat with model" \
"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" \
"install" "Install PyPI packages into llm env" \
"keys" "Manage stored API keys" \
"logs" "Explore logged prompts/responses" \
"models" "Manage available models" \
"openai" "Work with OpenAI API directly" \
"plugins" "List installed plugins" \
"similar" "Return top-N similar IDs for collection" \
"templates" "Manage stored prompt templates" \
"uninstall" "Uninstall Python packages from llm env"
end
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"
function __fish_llm_models
llm models |
string replace -r '^[^:]+: ([^ ]+)(?: \\(aliases: )?([^),]+,?)?+.*$' '$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

View File

@@ -2,18 +2,16 @@ function __fish_ollama_list
ollama list 2>/dev/null | tail -n +2 | string replace --regex "\s.*" ""
end
complete -f -c ollama
complete -c ollama -n __fish_use_subcommand -a serve -d "Start ollama"
complete -c ollama -n __fish_use_subcommand -a create -d "Create a model from a Modelfile"
complete -c ollama -n __fish_use_subcommand -a show -d "Show information for a model"
complete -c ollama -n __fish_use_subcommand -a run -d "Run a model"
complete -c ollama -n __fish_use_subcommand -a pull -d "Pull a model from a registry"
complete -c ollama -n __fish_use_subcommand -a push -d "Push a model to a registry"
complete -c ollama -n __fish_use_subcommand -a list -d "List models"
complete -c ollama -n __fish_use_subcommand -a cp -d "Copy a model"
complete -c ollama -n __fish_use_subcommand -a rm -d "Remove a model"
complete -c ollama -n __fish_use_subcommand -a help -d "Help about any command"
complete -c ollama -a serve -d "Start ollama"
complete -c ollama -a create -d "Create a model from a Modelfile"
complete -c ollama -a show -d "Show information for a model"
complete -c ollama -a run -d "Run a model"
complete -c ollama -a pull -d "Pull a model from a registry"
complete -c ollama -a push -d "Push a model to a registry"
complete -c ollama -a list -d "List models"
complete -c ollama -a cp -d "Copy a model"
complete -c ollama -a rm -d "Remove a model"
complete -c ollama -a help -d "Help about any command"
complete -c ollama -s h -l help -d "help for ollama"
complete -c ollama -s v -l version -d "Show version information"
complete -c ollama -f -a "(__fish_ollama_list)" --condition '__fish_seen_subcommand_from show'

View File

@@ -14,7 +14,6 @@ complete -f -c path -n "test (count (commandline -xpc)) -lt 2" -a sort -d 'Sort
complete -f -c path -n "test (count (commandline -xpc)) -ge 2" -s q -l quiet -d "Only return status, no output"
complete -f -c path -n "test (count (commandline -xpc)) -ge 2" -s z -l null-in -d "Handle NULL-delimited input"
complete -f -c path -n "test (count (commandline -xpc)) -ge 2" -s Z -l null-out -d "Print NULL-delimited output"
complete -f -c path -n "test (count (commandline -xpc)) -ge 2; and contains -- (commandline -xpc)[2] basename" -s E -l no-extension -d "Remove the extension"
complete -f -c path -n "test (count (commandline -xpc)) -ge 2; and contains -- (commandline -xpc)[2] filter is" -s v -l invert -d "Invert meaning of filters"
complete -f -c path -n "test (count (commandline -xpc)) -ge 2; and contains -- (commandline -xpc)[2] filter is" -s t -l type -d "Filter by type" -x -a '(__fish_append , file link dir block char fifo socket)'
complete -f -c path -n "test (count (commandline -xpc)) -ge 2; and contains -- (commandline -xpc)[2] filter is" -s f -d "Filter files"

View File

@@ -1,4 +1,4 @@
set -l rclone_version (rclone version | string match -rg 'rclone v?(.*)' | string split .)
set -l rclone_version (rclone version | string match -rg 'rclone v(.*)' | string split .)
or return
# Yes, rclone's parsing here has changed, now they *require* a `-` argument

View File

@@ -1 +0,0 @@
tailscale completion fish | source

View File

@@ -1,395 +1,2 @@
function __fish_wine_explorer__complete_desktop_arg
set -l current_token (commandline -tc)
switch "$current_token"
case '/desktop=*x*'
return
case '/desktop=*'
printf '%sx' "$current_token"
end
end
function __fish_msiexec__option_completion_condition
set -l token (commandline -oc)[3]
not string match --quiet --regex '^/(uninstall|[ipyz])$|^/[fjql]' -- "$token"
end
function __fish_msiexec_complete_option_arg
set -l current_token (commandline -tc)
switch "$current_token"
case '/f*'
set values 'p\tReinstall the file if it is missing' \
'o\tReinstall the file if it is missing or if any older version is installed' \
'e\tReinstall the file if it is missing, or if the installed version is equal or older' \
'd\tReinstall the file if it is missing or a different version is installed' \
'c\tReinstall the file if it is missing or the checksum does not match' \
'a\tReinstall all files' \
'u\tRewrite all required user registry entries' \
'm\tRewrite all required machine registry entries' \
's\tOverwrite any conflicting shortcuts' \
'v\tRecache the local installation package from the source installation package'
case '/q*'
set values 'n\tDisable UI' \
'b\tShow the basic UI' \
'r\tShow the reduced UI' \
'f\tShow the full UI'
case '/l*'
set values '*\tEnable all options except v and x' \
'i\tEnable status messages' \
'w\tEnable warning messages' \
'e\tEnable error messages' \
'a\tEnable messages for action startups' \
'r\tEnable messages for action records' \
'u\tEnable messages for user requests' \
'c\tEnable messages for initial UI parameters' \
'm\tEnable messages for out of memory errors' \
'o\tEnable messages for out of disk space errors' \
'p\tEnable messages for terminal properties' \
'v\tEnable verbose messages' \
'x\tEnable messages for debugging' \
'+\tAppend messages to a file' \
'!\tFlush each line of messages'
end
for value in $values
echo -e "$current_token$value"
end
end
function __fish_wine__complete_cmd_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from cmd'
complete -c $command -a '(__fish_cmd__complete_args)' -n $condition
set -l c_and_k_condition 'not __fish_seen_argument -w c -w k'
complete -c $command -a /c \
-d 'Run the command and stop' \
-n "$condition && $c_and_k_condition"
complete -c $command -a /k \
-d 'Run the command and continue' \
-n "$condition && $c_and_k_condition"
complete -c $command -a /s \
-d 'Modify the treatment of string after /c or /k' \
-n $condition
complete -c $command -a /q -d 'Turn echo off' -n $condition
complete -c $command -a /d \
-d 'Disable execution of AutoRun commands' \
-n $condition
set -l a_and_u_condition 'not __fish_seen_argument -w a -w u'
complete -c $command -a /a \
-d 'Format internal command output as ANSI' \
-n "$condition && $a_and_u_condition"
complete -c $command -a /u \
-d 'Format internal command output as Unicode' \
-n "$condition && $a_and_u_condition"
complete -c $command -a /t \
-d 'Set the background and foreground color' \
-n $condition
complete -c $command -a /e -d 'Manage command extensions' -n $condition
complete -c $command -a /f \
-d 'Manage file and directory name completion' \
-n $condition
complete -c $command -a /v \
-d 'Manage delayed environment variable expansion' \
-n $condition
complete -c $command -a '/?' -d 'Show help' -n $condition
end
function __fish_wine__complete_control_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from control'
complete -c $command \
-a 'COLOR DATE/TIME DESKTOP INTERNATIONAL KEYBOARD MOUSE PORTS PRINTERS' \
-n $condition
end
function __fish_wine__complete_eject_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from eject'
complete -c $command -s h -d 'Show help' -n $condition
complete -c $command -s a -d 'Eject all the CD drives' -n $condition
complete -c $command -s u -d 'Unmount the CD drives' -n $condition
end
function __fish_wine__complete_explorer_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from explorer'
complete -c $command -a '(__fish_wine_explorer__complete_desktop_arg)' \
-n $condition
complete -c $command -a /n -d 'Use the single pain view' -n $condition
complete -c $command -a /e, -d 'Use the default view' -n $condition
complete -c $command -a /root, -d 'Specify the root level of a view' \
-n $condition
complete -c $command -a /select, -d 'Specify the selection in a view' \
-n $condition
complete -c $command -a /desktop= -d 'Specify the desktop name' \
-n $condition
end
function __fish_wine__complete_msiexec_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from msiexec'
complete -c $command -a '/? /h' -d 'Show help' -n $condition
complete -c $command -a '(__fish_msiexec_complete_option_arg)' \
-n $condition
complete -c $command -a /i \
-d 'Install the software' \
-n "$condition && __fish_msiexec__option_completion_condition"
set -l a_condition '__fish_seen_argument -w i -w p'
complete -c $command -a /a \
-d 'Use the administrator network' \
-n "$condition && $a_condition"
complete -c $command -a /f \
-d 'Repair the installation of software' \
-n "$condition && __fish_msiexec__option_completion_condition"
complete -c $command -a /uninstall \
-d 'Uninstall the software' \
-n "$condition && __fish_msiexec__option_completion_condition"
complete -c $command -a /j \
-d 'Advertise the software' \
-n "$condition && __fish_msiexec__option_completion_condition"
complete -c $command -a /p \
-d 'Apply the patch to software' \
-n "$condition && __fish_msiexec__option_completion_condition"
complete -c $command -a /q \
-d 'Change the UI while installing software' \
-n "$condition && __fish_msiexec__option_completion_condition"
complete -c $command -a /l \
-d 'Change the logging' \
-n "$condition && __fish_msiexec__option_completion_condition"
complete -c $command -a /y \
-d 'Register the MSI service' \
-n "$condition && __fish_msiexec__option_completion_condition"
complete -c $command -a /z \
-d 'Register the MSI service' \
-n "$condition && __fish_msiexec__option_completion_condition"
end
function __fish_wine__complete_regedit_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from regedit'
complete -c $command -a '/?' -d 'Show help' -n $condition
complete -c $command -a /L \
-d 'Specify the location of system.dat file' \
-n $condition
complete -c $command -a /R \
-d 'Specify the location of user.dat file' \
-n $condition
complete -c $command -a /C \
-d 'Import contents of a registry file' \
-n $condition
complete -c $command -a /D \
-d 'Delete a registry key' \
-n $condition
complete -c $command -a /E \
-d 'Export contents to a registry file' \
-n $condition
complete -c $command -a /S \
-d 'Do not display messages' \
-n $condition
complete -c $command -a /V \
-d 'Launch the GUI in an advanced mode' \
-n $condition
complete -c $command -a '(__fish_reg__complete_keys)' \
-n $condition
end
function __fish_wine__complete_start_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from start'
complete -c $command -a '/?' -d 'Show help' -n $condition
complete -c $command -a /d \
-d 'Specify the directory for a program' \
-n $condition
complete -c $command -a /b \
-d "Don't create the new console for a progra" \
-n $condition
complete -c $command -a /i \
-d 'Clear the environment for a program' \
-n $condition
complete -c $command -a /min \
-d 'Start a program in the minimized window' \
-n $condition
complete -c $command -a /max \
-d 'Start a program in the minimized window' \
-n $condition
complete -c $command -a /low \
-d 'Start a program in the idle priority class' \
-n $condition
complete -c $command -a /normal \
-d 'Start a program in the normal priority class' \
-n $condition
complete -c $command -a /high \
-d 'Start a program in the high priority class' \
-n $condition
complete -c $command -a /realtime \
-d 'Start a program in the realtime priority class' \
-n $condition
complete -c $command -a /abovenormal \
-d 'Start a program in the abovenormal priority class' \
-n $condition
complete -c $command -a /belownormal \
-d 'Start a program in the belownormal priority class' \
-n $condition
complete -c $command -a /node \
-d 'Specify the NUMA node for a program' \
-n $condition
complete -c $command -a /affinity \
-d 'Specify the affinity mask for a program' \
-n $condition
complete -c $command -a /wait \
-d 'Wait for a program to exit' \
-n $condition
complete -c $command -a /unix \
-d 'Use the Unix filename for a program' \
-n $condition
end
function __fish_wine__complete_winemenubuilder_subcommand --argument-names command
complete -c $command -a /w \
-d 'Wait till the shortcut can be created' \
-n '__fish_seen_subcommand_from winemenubuilder'
end
function __fish_wine__complete_winepath_subcommand --argument-names command
set -l condition '__fish_seen_subcommand_from winepath'
complete -c $command -s h -d 'Show help' -n $condition
complete -c $command -s v -d 'Show version' -n $condition
complete -c $command -s u -l unix \
-d 'Convert a Windows path to the Unix one' \
-n $condition
complete -c $command -s w -l windows \
-d 'Convert a Unix path to the Windows one' \
-n $condition
complete -c $command -s l -l long \
-d 'Convert a Windows path to the long format' \
-n $condition
complete -c $command -s s -l short \
-d 'Convert a Windows path to the short format' \
-n $condition
end
set -l command wine
complete -c $command -f
complete -c $command -l help -d 'Show help'
complete -c $command -l version -d 'Show version'
set -l subcommands_with_descriptions 'cacls\t"Edit ACLs"' \
'clock\t"Open the clock"' \
'cmd\t"Open the command prompt"' \
'cmdlgtst\t"commdlg.dll test jig"' \
'control\t"Open control panel"' \
'eject\t"Eject optical discs"' \
'expand\t"Expand cabinet files"' \
'explorer\t"Open Explorer"' \
'hh\t"Open HTML help"' \
'icinfo\t"List installed video compressors"' \
'iexplore\t"Open Internet Explorer"' \
'lodctr\t"Load performance monitor counters"' \
'msiexec\t"Open an installer for .msi files"' \
'net\t"Manage services"' \
'notepad\t"Notepad, a simple text editor"' \
'oleview\t"Browse and explore COM objects as well as configure DCOM"' \
'progman\t"Open Program manager"' \
'reg\t"Edit registry though command line"' \
'regedit\t"Edit registry"' \
'regsvr32\t"Register OLE components in the registry"' \
'rpcss\t"Open rpcss.exe"' \
'rundll32\t"Load a DLL and run an entry point"' \
'secedit\t"Edit security configuration"' \
'services\t"Manages services"' \
'spoolsv\t"Print documents"' \
'start\t"Start a program or open a file in the program"' \
'svchost\t"Host process for services"' \
'taskmgr\t"Open Task Manager"' \
'uninstaller\t"Uninstall a program"' \
'unlodctr\t"Unload performance monitor counters"' \
'view\t"View metafiles"' \
'wineboot\t"Reboot Wine"' \
'winebrowser\t"Launch native OS browser or mail client"' \
'winecfg\t"Configure wine through a GUI"' \
'wineconsole\t"Open Windows console"' \
'winedbg\t"Open debugger core"' \
'winedevice\t"Manages devices"' \
'winefile\t"Open file explorer"' \
'winemenubuilder\t"Build Unix menu entries"' \
'winemine\t"classic minesweeper game"' \
'winepath\t"Translate between Windows and Unix paths formats"' \
'winetest\t"Run DLL conformance test programs"' \
'winevdm\t"Open DOS"' \
'winhelp\t"Open help"' \
'winhlp32\t"HOpen help"' \
'winver\t"Show about information"' \
'wordpad\t"Open WordPad"' \
'write\t"Open WordPad"' \
'xcopy\t"Run xcopy"'
set -l subcommands (string replace --regex '\\\t.+' '' -- $subcommands_with_descriptions)
set -l root_condition "not __fish_seen_subcommand_from $subcommands"
complete -c $command -a "$subcommands_with_descriptions" -n $root_condition
__fish_wine__complete_cmd_subcommand $command
__fish_wine__complete_control_subcommand $command
__fish_wine__complete_eject_subcommand $command
__fish_wine__complete_explorer_subcommand $command
__fish_wine__complete_msiexec_subcommand $command
__fish_wine__complete_regedit_subcommand $command
__fish_wine__complete_start_subcommand $command
__fish_wine__complete_winemenubuilder_subcommand $command
__fish_wine__complete_winepath_subcommand $command
complete -c wine -l help -d 'Show help and exit'
complete -c wine -l version -d 'Show version and exit'

View File

@@ -5,5 +5,4 @@ complete -c zed -s v -l version -d "Print Zed's version and the app path"
complete -c zed -l foreground -d "Run zed in the foreground (useful for debugging)"
complete -c zed -l zed -d "Custom path to Zed.app or the zed binary" -r
complete -c zed -l dev-server-token -d "Run zed in dev-server mode" -r
complete -c zed -l uninstall -d "Uninstall Zed from user system"
complete -c zed -s h -l help -d "Print help (see a summary with '-h')"

View File

@@ -36,7 +36,7 @@ if test $status -eq 0 -a (count $sysver) -eq 3
set age (path mtime -R -- $whatis)
end
MANPATH="$dir" MANPAGER=cat WHATISPAGER=cat apropos "$argv"
MANPATH="$dir" apropos "$argv"
if test $age -ge $max_age
test -d "$dir" || mkdir -m 700 -p $dir
@@ -48,6 +48,6 @@ else
function __fish_apropos
# we only ever prefix match for completions. This also ensures results for bare apropos <TAB>
# (apropos '' gives no results, but apropos '^' lists all manpages)
MANPAGER=cat WHATISPAGER=cat apropos "$argv"
apropos "$argv"
end
end

View File

@@ -1,18 +0,0 @@
function __fish_change_key_bindings --argument-names bindings
set -g __fish_active_key_bindings $bindings
# Allow the user to set the variable universally
set -l scope
set -q fish_key_bindings
or set scope -g
# We try to use `set --no-event`, but to avoid leaving the user without bindings
# if they run this with an older version we fall back on setting the variable
# with an event.
if ! set --no-event $scope fish_key_bindings $bindings 2>/dev/null
# This triggers the handler, which calls us again
set $scope fish_key_bindings $bindings
# unless the handler somehow doesn't exist, which would leave us without bindings.
# this happens in no-config mode.
functions -q __fish_reload_key_bindings
and return 1
end
end

View File

@@ -1,32 +0,0 @@
function __fish_cmd__complete_args -d 'Function to generate args'
set -l current_token (commandline -tc)
switch $current_token
case '/t:*'
echo -e '0\tBlack
1\tBlue
2\tGreen
3\tAqua
4\tRed
5\tPurple
6\tYellow
7\tWhite
8\tGray
9\tLight blue
A\tLight green
B\tLight aqua
C\tLight red
D\tLight purple
E\tLight yellow
F\tBright white' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
case '/e:*'
echo -e 'on\tEnable command extensions
off\tDisable command extensions' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
case '/f:*'
echo -e 'on\tEnable file and directory name completion
off\tDisable file and directory name completion' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
case '/v:*'
echo -e 'on\tEnable delayed environment variable expansion
off\tDisable delayed environment variable expansion' | awk -F '\t' "{ printf \"$current_token%s\t%s\n\", \$1, \$2 }"
end
end

View File

@@ -16,7 +16,7 @@
# impressive.
function __fish_complete_gpg -d "Internal function for gpg completion code deduplication" -a __fish_complete_gpg_command
if string match -q 'gpg (GnuPG) 1.*' ($__fish_complete_gpg_command --version 2>/dev/null)
if string match -q 'gpg (GnuPG) 1.*' ($__fish_complete_gpg_command --version)
complete -c $__fish_complete_gpg_command -l simple-sk-checksum -d 'Integrity protect secret keys by using a SHA-1 checksum'
complete -c $__fish_complete_gpg_command -l no-sig-create-check -d "Do not verify each signature right after creation"
complete -c $__fish_complete_gpg_command -l pgp2 -d "Set up all options to be as PGP 2.x compliant as possible"

View File

@@ -13,7 +13,7 @@ where:
or set -l iprefix ""
set -q prefix[1]
or set -l prefix ""
set -l pat "$(commandline -t)"
set -l pat (commandline -t)
#set -l pat $argv[5]
switch $pat
case "*$div*"

View File

@@ -1,5 +1,5 @@
function __fish_complete_lpr_option --description 'Complete lpr option'
set -l optstr "$(commandline -t)"
set -l optstr (commandline -t)
switch $optstr
case '*=*'
string split -m1 = -- "$optstr" | read -l opt val

View File

@@ -180,8 +180,12 @@ end" >$__fish_config_dir/config.fish
end
set -g __fish_active_key_bindings "$fish_key_bindings"
set -g fish_bind_mode default
# Redirect stderr per #1155
$fish_key_bindings 2>/dev/null
if test "$fish_key_bindings" = fish_default_key_bindings
# Redirect stderr per #1155
fish_default_key_bindings 2>/dev/null
else
$fish_key_bindings 2>/dev/null
end
# Load user key bindings if they are defined
if functions --query fish_user_key_bindings >/dev/null
fish_user_key_bindings 2>/dev/null

View File

@@ -2,7 +2,7 @@
# of the directory under the cursor.
function __fish_list_current_token -d "List contents of token under the cursor if it is a directory, otherwise list the contents of the current directory"
set -l val "$(commandline -t | string replace -r '^~' "$HOME")"
set -l val (commandline -t | string replace -r '^~' "$HOME")
set -l cmd
if test -d $val
set cmd ls $val

View File

@@ -1,30 +0,0 @@
function __fish_reg__complete_keys
set -l current_token (commandline -tc | string unescape)
set -l default_keys 'HKEY_CLASSES_ROOT\tThe information about file extension associations' \
'HKEY_CURRENT_USER\tThe information about a current user' \
'HKEY_LOCAL_MACHINE\tThe information about a current machine' \
'HKEY_USERS\tThe information about loaded users' \
'HKEY_CURRENT_CONFIG\tThe information about hardware used while startup' \
HKEY_DYN_DATA
if string match --quiet --entire --regex '\\\\' -- "$current_token"
set current_token (string replace --regex '\\\\[^\\\\]*$' '' -- "$current_token")
set -l keys (WINEDEBUG=-all wine reg query "$current_token" |
string replace --regex '\r' '' |
string match --entire --regex '\w')
test $pipestatus[1] != 0 && return
string join \n -- $keys |
string match --invert "$current_token" |
string replace --all --regex '[\\\\]+' '\\\\'
else
for key in $default_keys
echo -e $key
end
end
end
complete -c true -a '(__fish_reg__complete_keys)'

View File

@@ -108,12 +108,15 @@ function edit_command_buffer --description 'Edit the command buffer in an extern
eval set -l pos "$(cat $cursor_from_editor)"
if set -q pos[1] && test $pos[1] = $f
set -l line $pos[2]
set -l indent (math (string length -- "$raw_lines[$line]") - (string length -- "$unindented_lines[$line]"))
set -l indent (math (string length -- $raw_lines[$line]) - (string length -- $unindented_lines[$line]))
set -l column (math $pos[3] - $indent)
if not commandline --line $line 2>/dev/null
commandline -f end-of-buffer
else
commandline --column $column 2>/dev/null || commandline -f end-of-line
commandline -C 0
for _line in (seq $line)[2..]
commandline -f down-line
end
commandline -f beginning-of-line
for _column in (seq $column)[2..]
commandline -f forward-single-char
end
end
command rm $cursor_from_editor

View File

@@ -9,8 +9,25 @@ function fish_default_key_bindings -d "emacs-like key binds"
if not set -q argv[1]
bind --erase --all --preset # clear earlier bindings, if any
if test "$fish_key_bindings" != fish_default_key_bindings
__fish_change_key_bindings fish_default_key_bindings || return
set fish_bind_mode default
# Allow the user to set the variable universally
set -l scope
set -q fish_key_bindings
or set scope -g
true
# We try to use `set --no-event`, but to avoid leaving the user without bindings
# if they run this with an older version we fall back on setting the variable
# with an event.
if ! set --no-event $scope fish_key_bindings fish_default_key_bindings 2>/dev/null
# This triggers the handler, which calls us again
set $scope fish_key_bindings fish_default_key_bindings
# unless the handler somehow doesn't exist, which would leave us without bindings.
# this happens in no-config mode.
functions -q __fish_reload_key_bindings
and return
else
# (we need to set the bind mode to default)
set --no-event fish_bind_mode default
end
end
end
@@ -88,4 +105,6 @@ function fish_default_key_bindings -d "emacs-like key binds"
# the following to tell a console to paste:
$legacy_bind --preset $argv \e\x20ep fish_clipboard_paste
end
set -e -g fish_cursor_selection_mode
end

View File

@@ -180,10 +180,10 @@ if string match -q Darwin -- (uname) && string match -q /usr/bin/git -- (command
else
# git is installed, but on the first run it may be very slow as xcrun needs to populate the cache.
# Kick it off in the background to populate the cache.
/bin/sh -c '/usr/bin/git --version; touch /tmp/__fish_git_ready' &>/dev/null &
command git --version &>/dev/null &
disown $last_pid &>/dev/null
function __fish_git_prompt_ready
path is /tmp/__fish_git_ready || return 1
path is (xcrun --show-cache-path 2>/dev/null) || return 1
# git is ready, erase the function.
functions -e __fish_git_prompt_ready
return 0

View File

@@ -4,33 +4,35 @@ function fish_vi_cursor -d 'Set cursor shape for different vi modes'
return
end
# This is hard to test in expect, since the exact sequences depend on the environment.
# Instead disable it.
if set -q FISH_UNIT_TESTS_RUNNING
return
end
set -q fish_cursor_unknown
or set -g fish_cursor_unknown block
function __fish_vi_cursor --argument-names varname
if not set -q $varname
switch $varname
case fish_cursor_insert
__fish_cursor_xterm line
case fish_cursor_replace_one fish_cursor_replace
__fish_cursor_xterm underscore
case '*'
__fish_cursor_xterm $fish_cursor_unknown
end
return
end
__fish_cursor_xterm $$varname
end
echo "
function fish_vi_cursor_handle --on-variable fish_bind_mode --on-event fish_postexec --on-event fish_focus_in --on-event fish_read
set -l varname fish_cursor_\$fish_bind_mode
if not set -q \$varname
set varname fish_cursor_unknown
end
__fish_cursor_xterm \$\$varname
end
" | source
function fish_vi_cursor_handle --on-variable fish_bind_mode --on-event fish_postexec --on-event fish_focus_in --on-event fish_read
__fish_vi_cursor fish_cursor_$fish_bind_mode
end
function fish_vi_cursor_handle_preexec --on-event fish_preexec --on-event fish_exit
set -l varname fish_cursor_external
if not set -q $varname
set varname fish_cursor_default
end
__fish_vi_cursor $varname
end
echo "
function fish_vi_cursor_handle_preexec --on-event fish_preexec
set -l varname fish_cursor_external
if not set -q \$varname
set varname fish_cursor_default
end
if not set -q \$varname
set varname fish_cursor_unknown
end
__fish_cursor_xterm \$\$varname
end
" | source
end

View File

@@ -23,7 +23,22 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
# and without this would then have subtly broken bindings.
if test "$fish_key_bindings" != fish_vi_key_bindings
and test "$rebind" = true
__fish_change_key_bindings fish_vi_key_bindings || return
# Allow the user to set the variable universally
set -l scope
set -q fish_key_bindings
or set scope -g
true
# We try to use `set --no-event`, but to avoid leaving the user without bindings
# if they run this with an older version we fall back on setting the variable
# with an event.
if ! set --no-event $scope fish_key_bindings fish_vi_key_bindings 2>/dev/null
# This triggers the handler, which calls us again
set $scope fish_key_bindings fish_vi_key_bindings
# unless the handler somehow doesn't exist, which would leave us without bindings.
# this happens in no-config mode.
functions -q __fish_reload_key_bindings
and return
end
end
set -l init_mode insert
@@ -339,7 +354,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish'
set -g fish_cursor_selection_mode inclusive
function __fish_vi_key_bindings_on_mode_change --on-variable fish_bind_mode
switch $fish_bind_mode
case insert replace
case insert
set -g fish_cursor_end_mode exclusive
case '*'
set -g fish_cursor_end_mode inclusive

View File

@@ -95,11 +95,16 @@ function help --description 'Show help for the fish shell'
end
end
if not set -q fish_browser[1]
printf (_ '%s: Could not find a web browser.\n') help >&2
printf (_ 'Please try `BROWSER=some_browser help`, `man fish-doc`, or `man fish-tutorial`.\n\n') >&2
return 1
end
# In Cygwin, start the user-specified browser using cygstart,
# only if a Windows browser is to be used.
if type -q cygstart
if test "$fish_browser" != cygstart
and set -q fish_browser[1]
if test $fish_browser != cygstart
and not command -sq $fish_browser[1]
# Escaped quotes are necessary to work with spaces in the path
# when the command is finally eval'd.
@@ -181,7 +186,7 @@ function help --description 'Show help for the fish shell'
set -l version_string (string split . -f 1,2 -- $version | string join .)
set -l ext_url https://fishshell.com/docs/$version_string/$fish_help_page
set -l page_url
if set -q __fish_help_dir[1]; and test -f $__fish_help_dir/index.html; and not set -lq chromeos_linux_garcon
if test -f $__fish_help_dir/index.html; and not set -lq chromeos_linux_garcon
# Help is installed, use it
set page_url file://$__fish_help_dir/$fish_help_page
@@ -201,13 +206,6 @@ function help --description 'Show help for the fish shell'
set need_trampoline
end
if not set -q fish_browser[1]
printf (_ '%s: Could not find a web browser.\n') help >&2
printf (_ 'Please try `BROWSER=some_browser help`, `man fish-doc`, or `man fish-tutorial`.\n\n') >&2
printf (_ 'Or open %s in your browser of choice.\n') $ext_url >&2
return 1
end
if set -q need_trampoline[1]
# If string replace doesn't replace anything, we don't actually need a
# trampoline (they're only needed if there's a fragment in the path)

View File

@@ -1,6 +1,19 @@
#
# Wrap the builtin history command to provide additional functionality.
#
function __fish_unexpected_hist_args --no-scope-shadowing
if test -n "$search_mode"
or set -q show_time[1]
printf (_ "%ls: %ls: subcommand takes no options\n") $cmd $hist_cmd >&2
return 0
end
if set -q argv[1]
printf (_ "%ls: %ls: expected %d arguments; got %d\n") $cmd $hist_cmd 0 (count $argv) >&2
return 0
end
return 1
end
function history --description "display or manipulate interactive command history"
set -l cmd history
set -l options --exclusive 'c,e,p' --exclusive 'S,D,M,V,X'
@@ -69,6 +82,9 @@ function history --description "display or manipulate interactive command histor
switch $hist_cmd
case search # search the interactive command history
test -z "$search_mode"
and set search_mode --contains
if isatty stdout
set -l pager (__fish_anypager)
and isatty stdout
@@ -77,14 +93,14 @@ function history --description "display or manipulate interactive command histor
# If the user hasn't preconfigured less with the $LESS environment variable,
# we do so to have it behave like cat if output fits on one screen.
if not set -qx LESS
set -fx LESS --quit-if-one-screen
set -x LESS --quit-if-one-screen
# Also set --no-init for less < v530, see #8157.
if type -q less; and test (less --version | string match -r 'less (\d+)')[2] -lt 530 2>/dev/null
set LESS $LESS --no-init
set -x LESS $LESS --no-init
end
end
not set -qx LV # ask the pager lv not to strip colors
and set -fx LV -c
and set -x LV -c
builtin history search $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $argv | $pager
else
@@ -92,12 +108,17 @@ function history --description "display or manipulate interactive command histor
end
case delete # interactively delete history
# TODO: Fix this to deal with history entries that have multiple lines.
set -l searchterm $argv
if not set -q argv[1]
read -P"Search term: " searchterm
end
if test "$search_mode" = --exact
if test -z "$search_mode"
set search_mode --contains
end
if test $search_mode = --exact
builtin history delete $search_mode $_flag_case_sensitive -- $searchterm
builtin history save
return
@@ -176,38 +197,42 @@ function history --description "display or manipulate interactive command histor
end
case save # save our interactive command history to the persistent history
builtin history save $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $argv
__fish_unexpected_hist_args $argv
and return 1
builtin history save -- $argv
case merge # merge the persistent interactive command history with our history
builtin history merge $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $argv
__fish_unexpected_hist_args $argv
and return 1
builtin history merge -- $argv
case clear # clear the interactive command history
if test -n "$search_mode"
or set -q show_time[1]
printf (_ "%ls: %ls: subcommand takes no options\n") history $hist_cmd >&2
return 1
end
if set -q argv[1]
printf (_ "%ls: %ls: expected %d arguments; got %d\n") history $hist_cmd 0 (count $argv) >&2
return 1
end
__fish_unexpected_hist_args $argv
and return 1
printf (_ "If you enter 'yes' your entire interactive command history will be erased\n")
read --local --prompt "echo 'Are you sure you want to clear history? (yes/no) '" choice
if test "$choice" = yes
builtin history clear $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $argv
builtin history clear -- $argv
and printf (_ "Command history cleared!\n")
else
printf (_ "You did not say 'yes' so I will not clear your command history\n")
end
case clear-session # clears only session
builtin history clear-session $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $argv
and printf (_ "Command history for session cleared!\n")
__fish_unexpected_hist_args $argv
and return 1
builtin history clear-session -- $argv
printf (_ "Command history for session cleared!\n")
case append
set -l newitem $argv
if not set -q argv[1]
read -P "Command: " newitem
end
builtin history append $search_mode $show_time $max_count $_flag_case_sensitive $_flag_reverse $_flag_null -- $newitem
builtin history append -- $newitem
case '*'
printf "%ls: unexpected subcommand '%ls'\n" $cmd $hist_cmd
return 2

View File

@@ -43,8 +43,9 @@ pub trait Acceptor {
impl<T: Acceptor> Acceptor for Option<T> {
fn accept<'a>(&'a self, visitor: &mut dyn NodeVisitor<'a>, reversed: bool) {
if let Some(node) = self {
node.accept(visitor, reversed)
match self {
Some(node) => node.accept(visitor, reversed),
None => (),
}
}
}
@@ -89,8 +90,9 @@ trait AcceptorMut {
impl<T: AcceptorMut> AcceptorMut for Option<T> {
fn accept_mut(&mut self, visitor: &mut dyn NodeVisitorMut, reversed: bool) {
if let Some(node) = self {
node.accept_mut(visitor, reversed)
match self {
Some(node) => node.accept_mut(visitor, reversed),
None => (),
}
}
}
@@ -1644,16 +1646,16 @@ pub struct NotStatement {
parent: Option<*const dyn Node>,
/// Keyword, either not or exclam.
pub kw: KeywordNot,
pub time: Option<KeywordTime>,
pub variables: VariableAssignmentList,
pub time: Option<KeywordTime>,
pub contents: Statement,
}
implement_node!(NotStatement, branch, not_statement);
implement_acceptor_for_branch!(
NotStatement,
(kw: (KeywordNot)),
(time: (Option<KeywordTime>)),
(variables: (VariableAssignmentList)),
(time: (Option<KeywordTime>)),
(contents: (Statement)),
);
impl ConcreteNode for NotStatement {
@@ -1983,7 +1985,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
define_keyword_node!(KeywordFunction, kw_function);
define_keyword_node!(KeywordIf, kw_if);
define_keyword_node!(KeywordIn, kw_in);
define_keyword_node!(KeywordNot, kw_not, kw_exclam);
define_keyword_node!(KeywordNot, kw_not, kw_builtin, kw_exclam);
define_keyword_node!(KeywordSwitch, kw_switch);
define_keyword_node!(KeywordTime, kw_time);
define_keyword_node!(KeywordWhile, kw_while);

View File

@@ -21,8 +21,6 @@
#![allow(unstable_name_collisions)]
#![allow(clippy::uninlined_format_args)]
#[allow(unused_imports)]
use fish::future::IsSomeAnd;
use fish::{
ast::Ast,
builtins::shared::{
@@ -52,7 +50,7 @@
printf,
proc::{
get_login, is_interactive_session, mark_login, mark_no_exec, proc_init,
set_interactive_session, Pid,
set_interactive_session,
},
reader::{reader_init, reader_read, term_copy_modes},
signal::{signal_clear_cancel, signal_unblock_all},
@@ -73,135 +71,9 @@
const DOC_DIR: &str = env!("DOCDIR");
const DATA_DIR: &str = env!("DATADIR");
const DATA_DIR_SUBDIR: &str = env!("DATADIR_SUBDIR");
const SYSCONF_DIR: &str = env!("SYSCONFDIR");
const BIN_DIR: &str = env!("BINDIR");
#[cfg(feature = "installable")]
// Disable for clippy because otherwise it would require sphinx
#[cfg(not(clippy))]
fn install(confirm: bool) -> bool {
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "share/"]
struct Asset;
#[derive(RustEmbed)]
#[folder = "target/man/man1"]
#[prefix = "man/man1/"]
struct Docs;
use std::fs;
use std::io::ErrorKind;
use std::io::Write;
use std::io::{stderr, stdin};
let Some(home) = fish::env::get_home() else {
eprintln!("Can't find $HOME",);
return false;
};
let dir = PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR);
// TODO: Translation,
// FLOG?
// - Install: Translations
// - Install: Manpages (build via build.rs)
// - Don't install: __fish_build_paths.fish.in
if confirm {
if isatty(libc::STDIN_FILENO) {
eprintln!(
"This will write fish's data files to '{}'.\n\
Please enter 'yes' to continue.",
dir.display()
);
eprint!("> ");
let _ = stderr().flush();
}
let mut input = String::new();
if let Err(error) = stdin().read_line(&mut input) {
eprintln!("error: {error}")
}
if input != "yes\n" {
eprintln!("Exiting without writing any files\n");
return false;
}
} else {
eprintln!("Installing fish's data files to '{}'.", dir.display());
}
// Remove the install directory first, to clean out any removed files.
if let Err(err) = fs::remove_dir_all(dir.clone()) {
if err.kind() != ErrorKind::NotFound {
eprintln!("Removing '{}' failed: {}", dir.display(), err);
return false;
}
}
// TODO: These are duplicated, no idea how to extract
// them into a function
for file in Asset::iter() {
let path = dir.join(file.as_ref());
let Ok(_) = fs::create_dir_all(path.parent().unwrap()) else {
eprintln!(
"Creating directory '{}' failed",
path.parent().unwrap().display()
);
return false;
};
let res = File::create(&path);
let Ok(mut f) = res else {
eprintln!("Creating file '{}' failed", path.display());
continue;
};
// This should be impossible.
let d = Asset::get(&file).expect("File was somehow not included???");
if let Err(error) = f.write_all(&d.data) {
eprintln!("error: {error}");
return false;
}
}
for file in Docs::iter() {
let path = dir.join(file.as_ref());
let Ok(_) = fs::create_dir_all(path.parent().unwrap()) else {
eprintln!(
"Creating directory '{}' failed",
path.parent().unwrap().display()
);
return false;
};
let res = File::create(&path);
let Ok(mut f) = res else {
eprintln!("Creating file '{}' failed", path.display());
continue;
};
// This should be impossible.
let d = Docs::get(&file).expect("File was somehow not included???");
if let Err(error) = f.write_all(&d.data) {
eprintln!("error: {error}");
return false;
}
}
let verfile = dir.join("fish-install-version");
let res = File::create(&verfile);
if let Ok(mut f) = res {
f.write_all(fish::BUILD_VERSION.as_bytes())
.expect("FAILED TO WRITE");
} else {
eprintln!("Creating file '{}' failed", verfile.display());
};
return true;
}
#[cfg(any(clippy, not(feature = "installable")))]
fn install(_confirm: bool) -> bool {
eprintln!("Fish was built without support for self-installation");
return false;
}
/// container to hold the options specified within the command line
#[derive(Default, Debug)]
struct FishCmdOpts {
@@ -295,7 +167,7 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
data: manifest_dir.join("share"),
sysconf: manifest_dir.join("etc"),
doc: manifest_dir.join("user_doc/html"),
bin: Some(exec_path.parent().unwrap().to_owned()),
bin: exec_path.parent().unwrap().to_owned(),
}
}
@@ -307,7 +179,7 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
data: base_path.join("share/fish"),
sysconf: base_path.join("etc/fish"),
doc: base_path.join("share/doc/fish"),
bin: Some(base_path.join("bin")),
bin: base_path.join("bin"),
}
} else if exec_path.ends_with("fish") {
FLOG!(
@@ -319,7 +191,7 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
data: base_path.join("share"),
sysconf: base_path.join("etc"),
doc: base_path.join("user_doc/html"),
bin: Some(base_path.to_path_buf()),
bin: base_path.to_path_buf(),
}
}
@@ -335,31 +207,12 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
if !done {
// Fall back to what got compiled in.
let data = if cfg!(feature = "installable") {
let Some(home) = fish::env::get_home() else {
FLOG!(
error,
"Cannot find home directory and will refuse to read configuration"
);
return paths;
};
PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR)
} else {
PathBuf::from(DATA_DIR).join(DATA_DIR_SUBDIR)
};
let bin = if cfg!(feature = "installable") {
exec_path.parent().map(|x| x.to_path_buf())
} else {
Some(PathBuf::from(BIN_DIR))
};
FLOG!(config, "Using compiled in paths:");
paths = ConfigPaths {
data,
data: PathBuf::from(DATA_DIR).join("fish"),
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
doc: DOC_DIR.into(),
bin,
bin: BIN_DIR.into(),
}
}
@@ -370,11 +223,7 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
paths.data.display().to_string(),
paths.sysconf.display().to_string(),
paths.doc.display().to_string(),
paths
.bin
.clone()
.map(|x| x.display().to_string())
.unwrap_or("|not found|".to_string()),
paths.bin.display().to_string()
);
paths
@@ -410,60 +259,9 @@ fn source_config_in_directory(parser: &Parser, dir: &wstr) -> bool {
return true;
}
#[cfg(feature = "installable")]
fn check_version_file(paths: &ConfigPaths, datapath: &wstr) -> Option<bool> {
// (false-positive, is_none_or is a backport, this builds with 1.70)
#[allow(clippy::incompatible_msrv)]
if paths
.bin
.clone()
.is_none_or(|x| !x.starts_with(env!("CARGO_MANIFEST_DIR")))
{
// 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 version = std::fs::read_to_string(verfile).ok()?;
return Some(version == fish::BUILD_VERSION);
}
// When running from the manifest dir, we'll just run.
return Some(true);
}
/// Parse init files. exec_path is the path of fish executable as determined by argv[0].
fn read_init(parser: &Parser, paths: &ConfigPaths) {
let datapath = str2wcstring(paths.data.as_os_str().as_bytes());
#[cfg(feature = "installable")]
{
// If the version file is non-existent or out of date,
// we try to install automatically, but only if we're interactive.
// If we're not interactive, we still print an error later on pointing to `--install` if they don't exist,
// but don't complain if they're merely out-of-date.
// We do specifically check for a tty because we want to read input to confirm.
let v = check_version_file(paths, &datapath);
#[allow(clippy::incompatible_msrv)]
if v.is_none_or(|x| !x) && is_interactive_session() && isatty(libc::STDIN_FILENO) {
if v.is_none() {
FLOG!(
warning,
"Fish's asset files are missing. Trying to install them."
);
} else {
FLOG!(
warning,
"Fish's asset files are out of date. Trying to install them."
);
}
install(true);
// 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.
}
}
if !source_config_in_directory(parser, &datapath) {
// If we cannot read share/config.fish, our internal configuration,
// something is wrong.
@@ -472,14 +270,8 @@ fn read_init(parser: &Parser, paths: &ConfigPaths) {
let escaped_pathname = escape(&datapath);
FLOGF!(
error,
"Fish cannot find its asset files in '%ls'.\n\
Refusing to read configuration because of this.",
escaped_pathname,
);
#[cfg(feature = "installable")]
FLOG!(
error,
"If you installed via `cargo install`, please run `fish --install` and restart fish."
"Fish cannot find its asset files in '%ls'. Refusing to read configuration.",
escaped_pathname
);
return;
}
@@ -540,7 +332,6 @@ 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!("print-debug-categories"),
NoArgument,
@@ -575,9 +366,6 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
'f' => opts.features = w.woptarg.unwrap().to_owned(),
'h' => opts.batch_cmds.push("__fish_print_help fish".into()),
'i' => opts.is_interactive_session = true,
'I' => {
install(false);
}
'l' => opts.is_login = true,
'N' => {
opts.no_config = true;
@@ -669,6 +457,10 @@ fn main() {
}
fn throwing_main() -> i32 {
let mut args: Vec<WString> = env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
let mut res = 1;
signal_unblock_all();
@@ -682,9 +474,6 @@ fn throwing_main() -> i32 {
}
}
let mut args: Vec<WString> = env::args_os()
.map(|osstr| str2wcstring(osstr.as_bytes()))
.collect();
if args.is_empty() {
args.push("fish".into());
}
@@ -906,10 +695,7 @@ fn throwing_main() -> i32 {
parser.get_last_status()
};
event::fire(
parser,
Event::process_exit(Pid::new(getpid()).unwrap(), exit_status),
);
event::fire(parser, Event::process_exit(getpid(), exit_status));
// Trigger any exit handlers.
event::fire_generic(

View File

@@ -30,7 +30,6 @@
use fish::expand::INTERNAL_SEPARATOR;
use fish::fds::set_cloexec;
use fish::fprintf;
#[allow(unused_imports)]
use fish::future::{IsSomeAnd, IsSorted};
use fish::global_safety::RelaxedAtomicBool;
use fish::highlight::{colorize, highlight_shell, HighlightRole, HighlightSpec};

View File

@@ -1,6 +1,6 @@
// Implementation of the bg builtin.
use crate::proc::Pid;
use libc::pid_t;
use super::prelude::*;
@@ -81,19 +81,18 @@ pub fn bg(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Optio
// If one argument is not a valid pid (i.e. integer >= 0), fail without backgrounding anything,
// but still print errors for all of them.
let mut retval: Option<i32> = STATUS_CMD_OK;
let pids: Vec<Pid> = args[opts.optind..]
let pids: Vec<pid_t> = args[opts.optind..]
.iter()
.filter_map(|arg| match fish_wcstoi(arg).map(Pid::new) {
Ok(Some(pid)) => Some(pid),
_ => {
.map(|arg| {
fish_wcstoi(arg).unwrap_or_else(|_| {
streams.err.append(wgettext_fmt!(
"%ls: '%ls' is not a valid job specifier\n",
cmd,
arg
));
retval = STATUS_INVALID_ARGS;
None
}
0
})
})
.collect();

View File

@@ -7,8 +7,8 @@
use crate::operation_context::{no_cancel, OperationContext};
use crate::parse_constants::ParserTestErrorBits;
use crate::parse_util::{
parse_util_detect_errors, parse_util_get_offset_from_line, parse_util_job_extent,
parse_util_lineno, parse_util_process_extent, parse_util_token_extent,
parse_util_detect_errors, parse_util_job_extent, parse_util_lineno, parse_util_process_extent,
parse_util_token_extent,
};
use crate::proc::is_interactive_session;
use crate::reader::{
@@ -88,7 +88,7 @@ fn replace_part(
if search_field_mode {
commandline_set_search_field(out, Some(out_pos));
} else {
commandline_set_buffer(Some(out), Some(out_pos));
commandline_set_buffer(out, Some(out_pos));
}
}
@@ -198,7 +198,6 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
let mut selection_start_mode = false;
let mut selection_end_mode = false;
let mut line_mode = false;
let mut column_mode = false;
let mut search_mode = false;
let mut paging_mode = false;
let mut paging_full_mode = false;
@@ -230,7 +229,6 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
wopt(L!("selection-start"), ArgType::NoArgument, 'B'),
wopt(L!("selection-end"), ArgType::NoArgument, 'E'),
wopt(L!("line"), ArgType::NoArgument, 'L'),
wopt(L!("column"), ArgType::NoArgument, '\x05'),
wopt(L!("search-mode"), ArgType::NoArgument, 'S'),
wopt(L!("paging-mode"), ArgType::NoArgument, 'P'),
wopt(L!("paging-full-mode"), ArgType::NoArgument, 'F'),
@@ -277,7 +275,6 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
'B' => selection_start_mode = true,
'E' => selection_end_mode = true,
'L' => line_mode = true,
'\x05' => column_mode = true,
'S' => search_mode = true,
's' => selection_mode = true,
'P' => paging_mode = true,
@@ -311,7 +308,6 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|| token_mode.is_some()
|| cursor_mode
|| line_mode
|| column_mode
|| search_mode
|| paging_mode
|| selection_start_mode
@@ -367,9 +363,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
return STATUS_INVALID_ARGS;
}
if (search_mode || line_mode || column_mode || cursor_mode || paging_mode)
&& positional_args > 1
{
if (search_mode || line_mode || cursor_mode || paging_mode) && positional_args > 1 {
streams
.err
.append(wgettext_fmt!(BUILTIN_ERR_TOO_MANY_ARGUMENTS, cmd));
@@ -378,7 +372,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
}
if (buffer_part.is_some() || token_mode.is_some() || cut_at_cursor || search_field_mode)
&& (cursor_mode || line_mode||column_mode || search_mode || paging_mode || paging_full_mode)
&& (cursor_mode || line_mode || search_mode || paging_mode || paging_full_mode)
// Special case - we allow to get/set cursor position relative to the process/job/token.
&& ((buffer_part.is_none() && !search_field_mode) || !cursor_mode)
{
@@ -413,75 +407,11 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
let buffer_part = buffer_part.unwrap_or(TextScope::String);
if line_mode || column_mode {
if positional_args != 0 {
let arg = w.argv[w.wopt_index];
let new_coord = match fish_wcstol(arg) {
Err(_) => {
streams
.err
.append(wgettext_fmt!(BUILTIN_ERR_NOT_NUMBER, cmd, arg));
builtin_print_error_trailer(parser, streams.err, cmd);
0
}
Ok(num) => num - 1,
};
let Ok(new_coord) = usize::try_from(new_coord) else {
streams
.err
.append(wgettext_fmt!("%ls: line/column index starts at 1", cmd));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
};
let new_pos = if line_mode {
let Some(offset) = parse_util_get_offset_from_line(
&rstate.text,
i32::try_from(new_coord).unwrap(),
) else {
streams
.err
.append(wgettext_fmt!("%ls: there is no line %ls\n", cmd, arg));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
};
offset
} else {
let line_index =
i32::try_from(parse_util_lineno(&rstate.text, rstate.cursor_pos)).unwrap() - 1;
let line_offset =
parse_util_get_offset_from_line(&rstate.text, line_index).unwrap_or_default();
let next_line_offset =
parse_util_get_offset_from_line(&rstate.text, line_index + 1)
.unwrap_or(rstate.text.len());
if line_offset + new_coord > next_line_offset {
streams.err.append(wgettext_fmt!(
"%ls: column %ls exceeds line length\n",
cmd,
arg
));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
}
line_offset + new_coord
};
commandline_set_buffer(None, Some(new_pos));
} else {
streams.out.append(sprintf!(
"%d\n",
if line_mode {
parse_util_lineno(&rstate.text, rstate.cursor_pos)
} else {
rstate.cursor_pos + 1
- parse_util_get_offset_from_line(
&rstate.text,
i32::try_from(parse_util_lineno(&rstate.text, rstate.cursor_pos))
.unwrap(),
)
.unwrap_or_default()
}
));
}
if line_mode {
streams.out.append(sprintf!(
"%d\n",
parse_util_lineno(&rstate.text, rstate.cursor_pos)
));
return STATUS_CMD_OK;
}
@@ -631,7 +561,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
.saturating_add_signed(isize::try_from(new_pos).unwrap()),
current_buffer.len(),
);
commandline_set_buffer(None, Some(new_pos));
commandline_set_buffer(current_buffer.to_owned(), Some(new_pos));
} else {
streams.out.append(sprintf!("%lu\n", current_cursor_pos));
}

View File

@@ -3,7 +3,7 @@
use super::shared::{builtin_print_help, STATUS_CMD_ERROR, STATUS_INVALID_ARGS};
use crate::io::IoStreams;
use crate::parser::Parser;
use crate::proc::{add_disowned_job, Job, Pid};
use crate::proc::{add_disowned_job, Job};
use crate::{
builtins::shared::{HelpOnlyCmdOpts, STATUS_CMD_OK},
wchar::wstr,
@@ -24,7 +24,7 @@ fn disown_job(cmd: &wstr, streams: &mut IoStreams, j: &Job) {
if j.is_stopped() {
if let Some(pgid) = pgid {
unsafe {
libc::killpg(pgid.as_pid_t(), SIGCONT);
libc::killpg(pgid, SIGCONT);
}
}
streams.err.append(wgettext_fmt!(
@@ -80,48 +80,44 @@ pub fn disown(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O
retval = STATUS_CMD_ERROR;
}
} else {
let mut jobs = vec![];
retval = STATUS_CMD_OK;
// If one argument is not a valid pid (i.e. integer >= 0), fail without disowning anything,
// but still print errors for all of them.
// Non-existent jobs aren't an error, but information about them is useful.
let mut jobs: Vec<_> = args[1..]
.iter()
.filter_map(|arg| {
// Attempt to convert the argument to a PID.
match fish_wcstoi(arg).ok().and_then(Pid::new) {
None => {
// Invalid identifier
streams.err.append(wgettext_fmt!(
"%ls: '%ls' is not a valid job specifier\n",
cmd,
arg
));
retval = STATUS_INVALID_ARGS;
None
}
Some(pid) => parser.job_get_from_pid(pid).or_else(|| {
// Valid identifier but no such job
// Multiple PIDs may refer to the same job; include the job only once by using a set.
for arg in &args[1..] {
match fish_wcstoi(arg)
.ok()
.and_then(|pid| if pid < 0 { None } else { Some(pid) })
{
None => {
streams.err.append(wgettext_fmt!(
"%ls: '%ls' is not a valid job specifier\n",
cmd,
arg
));
retval = STATUS_INVALID_ARGS;
}
Some(pid) => {
if let Some(j) = parser.job_get_from_pid(pid) {
jobs.push(j);
} else {
streams.err.append(wgettext_fmt!(
"%ls: Could not find job '%d'\n",
cmd,
pid
));
None
}),
}
}
})
.collect();
}
}
if retval != STATUS_CMD_OK {
return retval;
}
// One PID/JID may be repeated or multiple PIDs may refer to the same job;
// include the job only once.
jobs.sort_unstable_by_key(|job| job.job_id());
jobs.dedup_by_key(|job| job.job_id());
// Disown all target jobs.
for j in jobs {
disown_job(cmd, streams, &j);

View File

@@ -1,7 +1,6 @@
//! Implementation of the fg builtin.
use crate::fds::make_fd_blocking;
use crate::proc::Pid;
use crate::reader::reader_write_title;
use crate::tokenizer::tok_command;
use crate::wutil::perror;
@@ -52,8 +51,9 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Optio
// try to locate the job $argv[1], since we need to determine which error message to
// emit (ambigous job specification vs malformed job id).
let mut found_job = false;
if let Ok(Some(pid)) = fish_wcstoi(argv[optind]).map(Pid::new) {
found_job = parser.job_get_from_pid(pid).is_some();
match fish_wcstoi(argv[optind]) {
Ok(pid) if pid > 0 => found_job = parser.job_get_from_pid(pid).is_some(),
_ => (),
};
if found_job {
@@ -82,15 +82,14 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Optio
builtin_print_error_trailer(parser, streams.err, cmd);
}
Ok(pid) => {
let raw_pid = pid;
let pid = Pid::new(pid.abs());
let j = pid.and_then(|pid| parser.job_get_with_index_from_pid(pid));
let pid = pid.abs();
let j = parser.job_get_with_index_from_pid(pid);
if j.as_ref()
.map_or(true, |(_pos, j)| !j.is_constructed() || j.is_completed())
{
streams
.err
.append(wgettext_fmt!("%ls: No suitable job: %d\n", cmd, raw_pid));
.append(wgettext_fmt!("%ls: No suitable job: %d\n", cmd, pid));
job_pos = None;
job = None
} else {
@@ -103,7 +102,7 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Optio
"it is not under job control\n"
),
cmd,
raw_pid,
pid,
j.command()
));
None

View File

@@ -3,14 +3,12 @@
use crate::common::{valid_func_name, valid_var_name};
use crate::complete::complete_add_wrapper;
use crate::env::environment::Environment;
use crate::env::is_read_only;
use crate::event::{self, EventDescription, EventHandler};
use crate::function;
use crate::global_safety::RelaxedAtomicBool;
use crate::nix::getpid;
use crate::parse_tree::NodeRef;
use crate::parser_keywords::parser_keywords_is_reserved;
use crate::proc::Pid;
use crate::signal::Signal;
use std::sync::Arc;
@@ -58,7 +56,7 @@ fn default() -> Self {
/// Return the internal_job_id for a pid, or None if none.
/// This looks through both active and finished jobs.
fn job_id_for_pid(pid: Pid, parser: &Parser) -> Option<u64> {
fn job_id_for_pid(pid: i32, parser: &Parser) -> Option<u64> {
if let Some(job) = parser.job_get_from_pid(pid) {
Some(job.internal_job_id)
} else {
@@ -149,7 +147,7 @@ fn parse_cmd_opts(
}
e = EventDescription::CallerExit { caller_id };
} else if opt == 'p' && woptarg == "%self" {
let pid = Pid::new(getpid());
let pid: i32 = getpid();
e = EventDescription::ProcessExit { pid };
} else {
let Ok(pid @ 0..) = fish_wcstoi(woptarg) else {
@@ -161,15 +159,12 @@ fn parse_cmd_opts(
return STATUS_INVALID_ARGS;
};
if opt == 'p' {
e = EventDescription::ProcessExit { pid: Pid::new(pid) };
e = EventDescription::ProcessExit { pid };
} else {
// TODO: rationalize why a default of 0 is sensible.
let internal_job_id = match Pid::new(pid) {
Some(pid) => job_id_for_pid(pid, parser).unwrap_or(0),
None => 0,
};
let internal_job_id = job_id_for_pid(pid, parser).unwrap_or(0);
e = EventDescription::JobExit {
pid: Pid::new(pid),
pid,
internal_job_id,
};
}
@@ -177,17 +172,8 @@ fn parse_cmd_opts(
opts.events.push(e);
}
'a' => {
let name = w.woptarg.unwrap().to_owned();
if is_read_only(&name) {
streams.err.append(wgettext_fmt!(
"%ls: variable '%ls' is read-only\n",
cmd,
name
));
return STATUS_INVALID_ARGS;
}
handling_named_arguments = true;
opts.named_arguments.push(name);
opts.named_arguments.push(w.woptarg.unwrap().to_owned());
}
'S' => {
opts.shadow_scope = false;
@@ -374,13 +360,13 @@ pub fn function(
// process has already exited, run it immediately (#7210).
for ed in &opts.events {
match *ed {
EventDescription::ProcessExit { pid: Some(pid) } => {
EventDescription::ProcessExit { pid } if pid != event::ANY_PID => {
let wh = parser.get_wait_handles().get_by_pid(pid);
if let Some(status) = wh.and_then(|wh| wh.status()) {
event::fire(parser, event::Event::process_exit(pid, status));
}
}
EventDescription::JobExit { pid: Some(pid), .. } => {
EventDescription::JobExit { pid, .. } if pid != event::ANY_PID => {
let wh = parser.get_wait_handles().get_by_pid(pid);
if let Some(wh) = wh {
if wh.is_completed() {

View File

@@ -8,28 +8,28 @@
#[derive(Default, Eq, PartialEq)]
enum HistCmd {
Search,
Delete,
Clear,
Merge,
Save,
HIST_SEARCH = 1,
HIST_DELETE,
HIST_CLEAR,
HIST_MERGE,
HIST_SAVE,
#[default]
None,
ClearSession,
Append,
HIST_UNDEF,
HIST_CLEAR_SESSION,
HIST_APPEND,
}
impl HistCmd {
fn to_wstr(&self) -> &'static wstr {
match self {
HistCmd::Search => L!("search"),
HistCmd::Delete => L!("delete"),
HistCmd::Clear => L!("clear"),
HistCmd::Merge => L!("merge"),
HistCmd::Save => L!("save"),
HistCmd::None => panic!(),
HistCmd::ClearSession => L!("clear-session"),
HistCmd::Append => L!("append"),
HistCmd::HIST_SEARCH => L!("search"),
HistCmd::HIST_DELETE => L!("delete"),
HistCmd::HIST_CLEAR => L!("clear"),
HistCmd::HIST_MERGE => L!("merge"),
HistCmd::HIST_SAVE => L!("save"),
HistCmd::HIST_UNDEF => panic!(),
HistCmd::HIST_CLEAR_SESSION => L!("clear-session"),
HistCmd::HIST_APPEND => L!("append"),
}
}
}
@@ -38,13 +38,13 @@ impl TryFrom<&wstr> for HistCmd {
type Error = ();
fn try_from(val: &wstr) -> Result<Self, ()> {
match val {
_ if val == "search" => Ok(HistCmd::Search),
_ if val == "delete" => Ok(HistCmd::Delete),
_ if val == "clear" => Ok(HistCmd::Clear),
_ if val == "merge" => Ok(HistCmd::Merge),
_ if val == "save" => Ok(HistCmd::Save),
_ if val == "clear-session" => Ok(HistCmd::ClearSession),
_ if val == "append" => Ok(HistCmd::Append),
_ if val == "search" => Ok(HistCmd::HIST_SEARCH),
_ if val == "delete" => Ok(HistCmd::HIST_DELETE),
_ if val == "clear" => Ok(HistCmd::HIST_CLEAR),
_ if val == "merge" => Ok(HistCmd::HIST_MERGE),
_ if val == "save" => Ok(HistCmd::HIST_SAVE),
_ if val == "clear-session" => Ok(HistCmd::HIST_CLEAR_SESSION),
_ if val == "append" => Ok(HistCmd::HIST_APPEND),
_ => Err(()),
}
}
@@ -91,7 +91,7 @@ fn set_hist_cmd(
sub_cmd: HistCmd,
streams: &mut IoStreams,
) -> bool {
if *hist_cmd != HistCmd::None {
if *hist_cmd != HistCmd::HIST_UNDEF {
streams.err.append(wgettext_fmt!(
BUILTIN_ERR_COMBO2_EXCLUSIVE,
cmd,
@@ -145,27 +145,27 @@ fn parse_cmd_opts(
while let Some(opt) = w.next_opt() {
match opt {
'\x01' => {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::Delete, streams) {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::HIST_DELETE, streams) {
return STATUS_CMD_ERROR;
}
}
'\x02' => {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::Search, streams) {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::HIST_SEARCH, streams) {
return STATUS_CMD_ERROR;
}
}
'\x03' => {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::Save, streams) {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::HIST_SAVE, streams) {
return STATUS_CMD_ERROR;
}
}
'\x04' => {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::Clear, streams) {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::HIST_CLEAR, streams) {
return STATUS_CMD_ERROR;
}
}
'\x05' => {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::Merge, streams) {
if !set_hist_cmd(cmd, &mut opts.hist_cmd, HistCmd::HIST_MERGE, streams) {
return STATUS_CMD_ERROR;
}
}
@@ -266,9 +266,14 @@ pub fn history(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
// search term).
let args = &args[optind..];
// Establish appropriate defaults.
if opts.hist_cmd == HistCmd::HIST_UNDEF {
opts.hist_cmd = HistCmd::HIST_SEARCH;
}
let mut status = STATUS_CMD_OK;
match opts.hist_cmd {
HistCmd::None | HistCmd::Search => {
HistCmd::HIST_SEARCH => {
if !history.search(
opts.search_type
.unwrap_or(history::SearchType::ContainsGlob),
@@ -284,7 +289,7 @@ pub fn history(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
status = STATUS_CMD_ERROR;
}
}
HistCmd::Delete => {
HistCmd::HIST_DELETE => {
// TODO: Move this code to the history module and support the other search types
// including case-insensitive matches. At this time we expect the non-exact deletions to
// be handled only by the history function's interactive delete feature.
@@ -306,21 +311,21 @@ pub fn history(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
history.remove(delete_string);
}
}
HistCmd::Clear => {
HistCmd::HIST_CLEAR => {
if check_for_unexpected_hist_args(&opts, cmd, args, streams) {
return STATUS_INVALID_ARGS;
}
history.clear();
history.save();
}
HistCmd::ClearSession => {
HistCmd::HIST_CLEAR_SESSION => {
if check_for_unexpected_hist_args(&opts, cmd, args, streams) {
return STATUS_INVALID_ARGS;
}
history.clear_session();
history.save();
}
HistCmd::Merge => {
HistCmd::HIST_MERGE => {
if check_for_unexpected_hist_args(&opts, cmd, args, streams) {
return STATUS_INVALID_ARGS;
}
@@ -334,13 +339,14 @@ pub fn history(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) ->
}
history.incorporate_external_changes();
}
HistCmd::Save => {
HistCmd::HIST_SAVE => {
if check_for_unexpected_hist_args(&opts, cmd, args, streams) {
return STATUS_INVALID_ARGS;
}
history.save();
}
HistCmd::Append => {
HistCmd::HIST_UNDEF => panic!("Unexpected HIST_UNDEF seen"),
HistCmd::HIST_APPEND => {
for &arg in args {
history.add_commandline(arg.to_owned());
}

View File

@@ -8,7 +8,7 @@
use crate::io::IoStreams;
use crate::job_group::{JobId, MaybeJobId};
use crate::parser::Parser;
use crate::proc::{clock_ticks_to_seconds, have_proc_stat, proc_get_jiffies, Job, Pid};
use crate::proc::{clock_ticks_to_seconds, have_proc_stat, proc_get_jiffies, Job, INVALID_PID};
use crate::wchar_ext::WExt;
use crate::wgetopt::{wopt, ArgType, WGetopter, WOption};
use crate::wutil::wgettext;
@@ -19,6 +19,7 @@
};
use libc::c_int;
use std::num::NonZeroU32;
use std::sync::atomic::Ordering;
/// Print modes for the jobs builtin.
@@ -35,9 +36,9 @@ enum JobsPrintMode {
/// This may exceed 1 if there are multiple CPUs!
fn cpu_use(j: &Job) -> f64 {
let mut u = 0.0;
for p in j.external_procs() {
for p in j.processes() {
let now = timef();
let jiffies = proc_get_jiffies(p.pid.load().unwrap());
let jiffies = proc_get_jiffies(p.pid.load(Ordering::Relaxed));
let last_jiffies = p.last_times.get().jiffies;
let since = now - last_jiffies as f64;
if since > 0.0 && jiffies > last_jiffies {
@@ -49,10 +50,12 @@ fn cpu_use(j: &Job) -> f64 {
/// Print information about the specified job.
fn builtin_jobs_print(j: &Job, mode: JobsPrintMode, header: bool, streams: &mut IoStreams) {
let pgid = match j.get_pgid() {
Some(pgid) => pgid.to_string(),
None => "-".to_string(),
};
let mut pgid = INVALID_PID;
{
if let Some(job_pgid) = j.get_pgid() {
pgid = job_pgid;
}
}
let mut out = WString::new();
match mode {
@@ -67,7 +70,7 @@ fn builtin_jobs_print(j: &Job, mode: JobsPrintMode, header: bool, streams: &mut
out += wgettext!("State\tCommand\n");
}
sprintf!(=> &mut out, "%d\t%s\t", j.job_id(), pgid);
sprintf!(=> &mut out, "%d\t%d\t", j.job_id(), pgid);
if have_proc_stat() {
sprintf!(=> &mut out, "%.0f%%\t", 100.0 * cpu_use(j));
@@ -94,7 +97,7 @@ fn builtin_jobs_print(j: &Job, mode: JobsPrintMode, header: bool, streams: &mut
// Print table header before first job.
out += wgettext!("Group\n");
}
out += &sprintf!("%s\n", pgid)[..];
out += &sprintf!("%d\n", pgid)[..];
streams.out.append(out);
}
JobsPrintMode::PrintPid => {
@@ -103,8 +106,8 @@ fn builtin_jobs_print(j: &Job, mode: JobsPrintMode, header: bool, streams: &mut
out += wgettext!("Process\n");
}
for p in j.external_procs() {
out += &sprintf!("%d\n", p.pid.load().unwrap())[..];
for p in j.processes() {
out += &sprintf!("%d\n", p.pid.load(Ordering::Relaxed))[..];
}
streams.out.append(out);
}
@@ -211,7 +214,7 @@ pub fn jobs(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
}
}
} else {
match fish_wcstoi(arg).ok().and_then(Pid::new) {
match fish_wcstoi(arg).ok().filter(|&pid| pid >= 0) {
None => {
streams.err.append(wgettext_fmt!(
"%ls: '%ls' is not a valid process id\n",

View File

@@ -239,13 +239,6 @@ fn verify_numeric(&mut self, s: &wstr, end: &wstr, errcode: Option<Error>) {
}
}
fn handle_sprintf_error(&mut self, err: fish_printf::Error) {
match err {
fish_printf::Error::Overflow => self.fatal_error(wgettext!("Number out of range")),
_ => panic!("unhandled error: {err:?}"),
}
}
/// Evaluate a printf conversion specification. SPEC is the start of the directive, and CONVERSION
/// specifies the type of conversion. SPEC does not include any length modifier or the
/// conversion specifier itself. FIELD_WIDTH and PRECISION are the field width and
@@ -275,7 +268,7 @@ macro_rules! append_output_fmt {
$fmt,
&self.locale,
&mut [$($arg.to_arg()),*]
).err().map(|err| self.handle_sprintf_error(err));
).expect("sprintf failed");
}
}
}

View File

@@ -135,14 +135,22 @@ fn parse_ull(streams: &mut IoStreams, cmd: &wstr, num: &wstr) -> Result<u64, wut
}
}
let (start, end) = if start <= end {
(start, end)
} else {
(end, start)
};
if end <= start {
streams
.err
.append(wgettext_fmt!("%ls: END must be greater than START\n", cmd,));
return STATUS_INVALID_ARGS;
}
// Using abs_diff() avoids an i64 overflow if start is i64::MIN and end is i64::MAX
let possibilities = end.abs_diff(start) / step;
if possibilities == 0 {
streams.err.append(wgettext_fmt!(
"%ls: range contains only one possible value\n",
cmd,
));
return STATUS_INVALID_ARGS;
}
let rand = {
let mut engine = RNG.lock().unwrap();

View File

@@ -12,7 +12,6 @@
use crate::env::Environment;
use crate::env::READ_BYTE_LIMIT;
use crate::env::{EnvVar, EnvVarFlags};
use crate::input_common::terminal_protocols_disable_ifn;
use crate::libc::MB_CUR_MAX;
use crate::nix::isatty;
use crate::reader::commandline_set_buffer;
@@ -234,7 +233,7 @@ fn read_interactive(
// Keep in-memory history only.
reader_push(parser, L!(""), conf);
commandline_set_buffer(Some(commandline.to_owned()), None);
commandline_set_buffer(commandline.to_owned(), None);
let mline = {
let _interactive = scoped_push_replacer(
@@ -244,7 +243,6 @@ fn read_interactive(
reader_readline(parser, nchars)
};
terminal_protocols_disable_ifn();
if let Some(line) = mline {
*buff = line;
if nchars > 0 && nchars < buff.len() {

View File

@@ -83,11 +83,15 @@ pub fn source(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O
Some(func_filename.clone()),
);
// Construct argv for the sourced file from our remaining args.
// Construct argv from our null-terminated list.
// This is slightly subtle. If this is a bare `source` with no args then `argv + optind` already
// points to the end of argv. Otherwise we want to skip the file name to get to the args if any.
let mut argv_list: Vec<WString> = vec![];
let remaining_args = &args[optind + if argc == optind { 0 } else { 1 }..];
let argv_list = remaining_args.iter().map(|&arg| arg.to_owned()).collect();
#[allow(clippy::unnecessary_to_owned)]
for arg in remaining_args.iter().copied() {
argv_list.push(arg.to_owned());
}
parser.vars().set_argv(argv_list);
let mut retval = reader_read(parser, fd, streams.io_chain);
@@ -105,5 +109,7 @@ pub fn source(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O
retval = parser.get_last_status();
}
// Do not close fd after calling reader_read. reader_read automatically closes it before calling
// eval.
Some(retval)
}

View File

@@ -305,16 +305,7 @@ pub fn ulimit(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O
None => return STATUS_CMD_ERROR,
}
} else if let Ok(limit) = fish_wcstol(w.argv[w.wopt_index]) {
let Some(x) = get_multiplier(what).checked_mul(limit as rlim_t) else {
streams.err.append(wgettext_fmt!(
"%ls: Invalid limit '%ls'\n",
cmd,
w.argv[w.wopt_index]
));
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
};
x
limit as rlim_t * get_multiplier(what)
} else {
streams.err.append(wgettext_fmt!(
"%ls: Invalid limit '%ls'\n",

View File

@@ -1,5 +1,7 @@
use libc::pid_t;
use super::prelude::*;
use crate::proc::{proc_wait_any, Job, Pid};
use crate::proc::{proc_wait_any, Job};
use crate::signal::SigChecker;
use crate::wait_handle::{WaitHandleRef, WaitHandleStore};
@@ -23,7 +25,7 @@ fn iswnumeric(s: &wstr) -> bool {
#[derive(Copy, Clone)]
enum WaitHandleQuery<'a> {
Pid(Pid),
Pid(pid_t),
ProcName(&'a wstr),
}
@@ -165,6 +167,8 @@ pub fn wait(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
return STATUS_CMD_OK;
}
crate::input_common::terminal_protocols_disable_ifn();
if w.wopt_index == argc {
// No jobs specified.
// Note this may succeed with an empty wait list.
@@ -177,15 +181,15 @@ pub fn wait(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Opt
for item in &argv[optind..argc] {
if iswnumeric(item) {
// argument is pid
let mpid: i32 = fish_wcstoi(item).unwrap_or(-1);
let Some(mpid) = Pid::new(mpid) else {
let mpid: pid_t = fish_wcstoi(item).unwrap_or(-1);
if mpid <= 0 {
streams.err.append(wgettext_fmt!(
"%ls: '%ls' is not a valid process id\n",
cmd,
item,
));
continue;
};
}
if !find_wait_handles(WaitHandleQuery::Pid(mpid), parser, &mut wait_handles) {
streams.err.append(wgettext_fmt!(
"%ls: Could not find a job with process id '%d'\n",

View File

@@ -252,7 +252,7 @@ fn try_parse_special(special: &wstr) -> Option<Self> {
/// - `#F3A035`
/// - `FA3`
/// - `F3A035`
///
/// Parses input in the form of `#RGB` or `#RRGGBB` with an optional single leading `#` into
/// an instance of [`RgbColor`].
///

View File

@@ -196,7 +196,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
);
let mut need_escape = false;
let mut need_complex_escape = no_tilde && input.char_at(0) == '~';
let mut need_complex_escape = false;
let mut double_quotes = 0;
let mut single_quotes = 0;
let mut dollars = 0;
@@ -1942,8 +1942,8 @@ pub trait Named {
fn name(&self) -> &'static wstr;
}
/// Return a reference to the first entry with the given name, assuming the entries are sorted by
/// name. Return None if not found.
/// Return a pointer to the first entry with the given name, assuming the entries are sorted by
/// name. Return nullptr if not found.
pub fn get_by_sorted_name<T: Named>(name: &wstr, vals: &'static [T]) -> Option<&'static T> {
match vals.binary_search_by_key(&name, |val| val.name()) {
Ok(index) => Some(&vals[index]),

View File

@@ -512,17 +512,14 @@ fn compare_completions_by_tilde(a: &Completion, b: &Completion) -> Ordering {
/// Unique the list of completions, without perturbing their order.
fn unique_completions_retaining_order(comps: &mut Vec<Completion>) {
let mut seen = HashSet::with_capacity(comps.len());
let removals = comps.iter().enumerate().fold(vec![], |mut v, (i, c)| {
if !seen.insert(&c.completion) {
v.push(i);
}
v
});
// Remove in reverse order so the indexes remain valid
for idx in removals.iter().rev() {
comps.remove(*idx);
}
let pred = |c: &Completion| {
// Keep (return true) if insertion succeeds.
// todo!("don't clone here");
seen.insert(c.completion.to_owned())
};
comps.retain(pred);
}
/// Sorts and removes any duplicate completions in the completion list, then puts them in priority
@@ -1284,14 +1281,14 @@ fn complete_param_for_command(
// Check if we are entering a combined option and argument (like --color=auto or
// -I/usr/include).
for o in &options {
let arg_offset = if o.typ == CompleteOptionType::Short {
let arg = if o.typ == CompleteOptionType::Short {
let Some(short_opt_pos) = short_opt_pos else {
continue;
};
if o.option.char_at(0) != s.char_at(short_opt_pos) {
continue;
}
Some(short_opt_pos + 1)
Some(s.slice_from(short_opt_pos + 1))
} else {
param_match2(o, s)
};
@@ -1304,7 +1301,7 @@ fn complete_param_for_command(
.get_or_insert(o.result_mode.requires_param) &=
o.result_mode.requires_param;
}
if let Some(arg_offset) = arg_offset {
if let Some(arg) = arg {
if o.result_mode.requires_param {
use_common = false;
}
@@ -1314,14 +1311,7 @@ fn complete_param_for_command(
if o.result_mode.force_files {
has_force = true;
}
let (arg_prefix, arg) = s.split_once(arg_offset);
let first_new = self.completions.completions.len();
self.complete_from_args(arg, &o.comp, o.localized_desc(), o.flags);
for compl in &mut self.completions.completions[first_new..] {
if compl.replaces_token() {
compl.completion.insert_utfstr(0, arg_prefix);
}
}
}
}
}
@@ -2220,7 +2210,7 @@ fn param_match(e: &CompleteEntryOpt, optstr: &wstr) -> bool {
}
/// Test if a string is an option with an argument, like --color=auto or -I/usr/include.
fn param_match2(e: &CompleteEntryOpt, optstr: &wstr) -> Option<usize> {
fn param_match2<'s>(e: &CompleteEntryOpt, optstr: &'s wstr) -> Option<&'s wstr> {
// We may get a complete_entry_opt_t with no options if it's just arguments.
if e.option.is_empty() {
return None;
@@ -2245,7 +2235,7 @@ fn param_match2(e: &CompleteEntryOpt, optstr: &wstr) -> Option<usize> {
return None;
}
cursor += 1;
Some(cursor)
Some(optstr.slice_from(cursor))
}
/// Parses a token of short options plus one optional parameter like

View File

@@ -1,4 +1,3 @@
#[allow(unused_imports)]
use crate::future::IsSomeAnd;
use crate::highlight::HighlightSpec;
use crate::wchar::prelude::*;

View File

@@ -452,39 +452,6 @@ fn get_hostname_identifier() -> Option<WString> {
}
}
/// Get values for $HOME via getpwuid,
/// without trusting $USER or $HOME.
pub fn get_home() -> Option<String> {
let uid: uid_t = geteuid();
let mut userinfo: MaybeUninit<libc::passwd> = MaybeUninit::uninit();
let mut result: *mut libc::passwd = std::ptr::null_mut();
let mut buf = [0 as libc::c_char; 8192];
// We need to get the data via the uid and don't trust $USER.
let retval = unsafe {
libc::getpwuid_r(
uid,
userinfo.as_mut_ptr(),
buf.as_mut_ptr(),
buf.len(),
&mut result,
)
};
if retval != 0 || result.is_null() {
return None;
}
let userinfo = unsafe { userinfo.assume_init() };
if !userinfo.pw_dir.is_null() {
let home = unsafe { CStr::from_ptr(userinfo.pw_dir) };
let home = home.to_str().ok().map(|x| x.to_owned());
home
} else {
None
}
}
/// Set up the USER and HOME variable.
fn setup_user(vars: &EnvStack) {
let uid: uid_t = geteuid();
@@ -628,49 +595,44 @@ pub fn env_init(paths: Option<&ConfigPaths>, do_uvars: bool, default_paths: bool
.set(inherited_vars)
.expect("env_init is being called multiple times");
// Set $USER, $HOME and $EUID
// This involves going to passwd and stuff.
vars.set_one(L!("EUID"), EnvMode::GLOBAL, geteuid().to_wstring());
setup_user(vars);
if let Some(paths) = paths {
let datadir = str2wcstring(paths.data.as_os_str().as_bytes());
vars.set_one(FISH_DATADIR_VAR, EnvMode::GLOBAL, datadir.clone());
vars.set_one(
FISH_DATADIR_VAR,
EnvMode::GLOBAL,
str2wcstring(paths.data.as_os_str().as_bytes()),
);
vars.set_one(
FISH_SYSCONFDIR_VAR,
EnvMode::GLOBAL,
str2wcstring(paths.sysconf.as_os_str().as_bytes()),
);
if !cfg!(feature = "installable") {
vars.set_one(
FISH_HELPDIR_VAR,
EnvMode::GLOBAL,
str2wcstring(paths.doc.as_os_str().as_bytes()),
);
} else {
vars.set_empty(FISH_HELPDIR_VAR, EnvMode::GLOBAL);
}
if let Some(bp) = &paths.bin {
vars.set_one(
FISH_BIN_DIR,
EnvMode::GLOBAL,
str2wcstring(bp.as_os_str().as_bytes()),
);
} else {
vars.set_empty(FISH_BIN_DIR, EnvMode::GLOBAL);
};
vars.set_one(
FISH_HELPDIR_VAR,
EnvMode::GLOBAL,
str2wcstring(paths.doc.as_os_str().as_bytes()),
);
vars.set_one(
FISH_BIN_DIR,
EnvMode::GLOBAL,
str2wcstring(paths.bin.as_os_str().as_bytes()),
);
if default_paths {
let mut scstr = datadir;
// This is generated by PathBuf.join() everywhere currently
assert!(!scstr.ends_with("/"));
scstr.push_str("/functions");
vars.set_one(L!("fish_function_path"), EnvMode::GLOBAL, scstr);
let mut scstr = paths.data.clone();
scstr.push("functions");
vars.set_one(
L!("fish_function_path"),
EnvMode::GLOBAL,
str2wcstring(scstr.as_os_str().as_bytes()),
);
}
}
// Set $USER, $HOME and $EUID
// This involves going to passwd and stuff.
vars.set_one(L!("EUID"), EnvMode::GLOBAL, geteuid().to_wstring());
setup_user(vars);
let user_config_dir = path_get_config();
vars.set_one(
FISH_CONFIG_DIR,

View File

@@ -344,8 +344,8 @@ pub fn set_last_statuses(&mut self, s: Statuses) {
}
fn try_get_computed(&self, key: &wstr) -> Option<EnvVar> {
let ev = ElectricVar::for_name(key)?;
if !ev.computed() {
let ev = ElectricVar::for_name(key);
if ev.is_none() || !ev.unwrap().computed() {
return None;
}
@@ -994,8 +994,8 @@ fn set_universal(&mut self, key: &wstr, mut val: Vec<WString>, query: Query) {
let mut exports = false;
if query.has_export_unexport {
exports = query.exports;
} else if let Some(v) = oldvar {
exports = v.exports();
} else if oldvar.is_some() {
exports = oldvar.unwrap().exports();
}
// Resolve whether to be a path variable.
@@ -1003,8 +1003,8 @@ fn set_universal(&mut self, key: &wstr, mut val: Vec<WString>, query: Query) {
let pathvar;
if query.has_pathvar_unpathvar {
pathvar = query.pathvar;
} else if let Some(v) = oldvar {
pathvar = v.is_pathvar();
} else if oldvar.is_some() {
pathvar = oldvar.unwrap().is_pathvar();
} else {
pathvar = variable_should_auto_pathvar(key);
}

8
src/env/var.rs vendored
View File

@@ -50,10 +50,10 @@ fn from(val: EnvMode) -> Self {
/// env_init.
#[derive(Default)]
pub struct ConfigPaths {
pub data: PathBuf, // e.g., /usr/local/share
pub sysconf: PathBuf, // e.g., /usr/local/etc
pub doc: PathBuf, // e.g., /usr/local/share/doc/fish
pub bin: Option<PathBuf>, // e.g., /usr/local/bin
pub data: PathBuf, // e.g., /usr/local/share
pub sysconf: PathBuf, // e.g., /usr/local/etc
pub doc: PathBuf, // e.g., /usr/local/share/doc/fish
pub bin: PathBuf, // e.g., /usr/local/bin
}
/// A collection of status and pipestatus.

View File

@@ -501,6 +501,62 @@ fn update_fish_color_support(vars: &EnvStack) {
crate::output::set_color_support(color_support);
}
/// Try to initialize the terminfo/curses subsystem using our fallback terminal name. Do not set
/// `$TERM` to our fallback. We're only doing this in the hope of getting a functional shell.
/// If we launch an external command that uses `$TERM`, it should get the same value we were given,
/// if any.
fn initialize_curses_using_fallbacks(vars: &EnvStack) {
// xterm-256color is the most used terminal type by a massive margin, especially counting
// terminals that are mostly compatible.
const FALLBACKS: [&str; 4] = ["xterm-256color", "xterm", "ansi", "dumb"];
let current_term = vars
.get_unless_empty(L!("TERM"))
.map(|v| v.as_string())
.unwrap_or(Default::default());
let mut success = false;
if current_term == "xterm-256color" {
// If we have xterm-256color, let's just use our hard-coded version
// instead of trying to read xterm or "ansi".
// It's almost certain we can't find terminfo.
FLOG!(
term_support,
"Could not read xterm-256color. Using fallback."
);
curses::setup_fallback_term();
return;
}
for term in FALLBACKS {
// If $TERM is already set to the fallback name we're about to use, there's no point in
// seeing if the fallback name can be used.
if current_term == term {
continue;
}
// `term` here is one of our hard-coded strings above; we can unwrap because we can
// guarantee it doesn't contain any interior NULs.
success = curses::setup(Some(term), |term| apply_term_hacks(vars, term)).is_some();
if is_interactive_session() && success {
FLOG!(warning, wgettext!("Using fallback terminal type"), term);
}
if success {
break;
}
}
if !success {
if is_interactive_session() {
FLOG!(
warning,
wgettext!("Could not get any terminfo database, falling back to hardcoded xterm-256color values"),
);
}
curses::setup_fallback_term();
}
}
/// Apply any platform- or environment-specific hacks to our curses [`Term`] instance.
fn apply_term_hacks(vars: &EnvStack, term: &mut Term) {
if cfg!(target_os = "macos") {
@@ -562,21 +618,20 @@ fn init_curses(vars: &EnvStack) {
let term = vars.get_unless_empty(L!("TERM")).map(|v| v.as_string());
// We do not warn for xterm-256color at all, we know that one.
if term != Some("xterm-256color".into()) {
FLOG!(warning, wgettext!("Could not set up terminal."));
if let Some(term) = term {
FLOG!(warning, wgettext!("TERM environment variable set to"), term);
FLOG!(
warning,
wgettext_fmt!("Could not set up terminal for $TERM '%ls'. Falling back to hardcoded xterm-256color values", term)
wgettext!("Check that this terminal type is supported on this system.")
);
} else {
FLOG!(
warning,
wgettext!("Could not set up terminal because $TERM is unset. Falling back to hardcoded xterm-256color values")
);
FLOG!(warning, wgettext!("TERM environment variable not set."));
}
}
}
curses::setup_fallback_term();
initialize_curses_using_fallbacks(vars);
}
// Configure hacks that apply regardless of whether we successfully init curses or not.

View File

@@ -4,6 +4,7 @@
//! defined when these functions produce output or perform memory allocations, since such functions
//! may not be safely called by signal handlers.
use libc::pid_t;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex};
@@ -12,7 +13,6 @@
use crate::io::{IoChain, IoStreams};
use crate::job_group::MaybeJobId;
use crate::parser::{Block, Parser};
use crate::proc::Pid;
use crate::signal::{signal_check_cancel, signal_handle, Signal};
use crate::termsize;
use crate::wchar::prelude::*;
@@ -27,6 +27,8 @@ pub enum event_type_t {
generic,
}
pub const ANY_PID: pid_t = 0;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum EventDescription {
/// Matches any event type (not always any event, as the function name may limit the choice as
@@ -38,13 +40,13 @@ pub enum EventDescription {
Variable { name: WString },
/// An event triggered by a process exit.
ProcessExit {
/// Process ID. Use [`None`] to match any pid.
pid: Option<Pid>,
/// Process ID. Use [`ANY_PID`] to match any pid.
pid: pid_t,
},
/// An event triggered by a job exit.
JobExit {
/// pid requested by the event, or [`None`] for all.
pid: Option<Pid>,
/// pid requested by the event, or [`ANY_PID`] for all.
pid: pid_t,
/// `internal_job_id` of the job to match.
/// If this is 0, we match either all jobs (`pid == ANY_PID`) or no jobs (otherwise).
internal_job_id: u64,
@@ -145,8 +147,8 @@ pub fn new(desc: EventDescription, name: Option<WString>) -> Self {
/// Return true if a handler is "one shot": it fires at most once.
fn is_one_shot(&self) -> bool {
match self.desc {
EventDescription::ProcessExit { pid } => pid.is_some(),
EventDescription::JobExit { pid, .. } => pid.is_some(),
EventDescription::ProcessExit { pid } => pid != ANY_PID,
EventDescription::JobExit { pid, .. } => pid != ANY_PID,
EventDescription::CallerExit { .. } => true,
EventDescription::Signal { .. }
| EventDescription::Variable { .. }
@@ -169,7 +171,7 @@ fn matches(&self, event: &Event) -> bool {
(
EventDescription::ProcessExit { pid },
EventDescription::ProcessExit { pid: ev_pid },
) => pid.is_none() || pid == ev_pid,
) => *pid == ANY_PID || pid == ev_pid,
(
EventDescription::JobExit {
pid,
@@ -179,7 +181,7 @@ fn matches(&self, event: &Event) -> bool {
internal_job_id: ev_internal_job_id,
..
},
) => pid.is_none() || internal_job_id == ev_internal_job_id,
) => *pid == ANY_PID || internal_job_id == ev_internal_job_id,
(
EventDescription::CallerExit { caller_id },
EventDescription::CallerExit {
@@ -224,9 +226,9 @@ pub fn variable_set(name: WString) -> Self {
}
}
pub fn process_exit(pid: Pid, status: i32) -> Self {
pub fn process_exit(pid: pid_t, status: i32) -> Self {
Self {
desc: EventDescription::ProcessExit { pid: Some(pid) },
desc: EventDescription::ProcessExit { pid },
arguments: vec![
"PROCESS_EXIT".into(),
pid.to_string().into(),
@@ -235,10 +237,10 @@ pub fn process_exit(pid: Pid, status: i32) -> Self {
}
}
pub fn job_exit(pgid: Pid, jid: u64) -> Self {
pub fn job_exit(pgid: pid_t, jid: u64) -> Self {
Self {
desc: EventDescription::JobExit {
pid: Some(pgid),
pid: pgid,
internal_job_id: jid,
},
arguments: vec![
@@ -380,19 +382,12 @@ pub fn get_desc(parser: &Parser, evt: &Event) -> WString {
format!("signal handler for {} ({})", signal.name(), signal.desc(),)
}
EventDescription::Variable { name } => format!("handler for variable '{name}'"),
EventDescription::ProcessExit { pid: None } => "exit handler for any process".to_string(),
EventDescription::ProcessExit { pid: Some(pid) } => {
format!("exit handler for process {pid}")
}
EventDescription::ProcessExit { pid } => format!("exit handler for process {pid}"),
EventDescription::JobExit { pid, .. } => {
if let Some(pid) = pid {
if let Some(job) = parser.job_get_from_pid(*pid) {
format!("exit handler for job {}, '{}'", job.job_id(), job.command())
} else {
format!("exit handler for job with pid {pid}")
}
if let Some(job) = parser.job_get_from_pid(*pid) {
format!("exit handler for job {}, '{}'", job.job_id(), job.command())
} else {
"exit handler for any job".to_string()
format!("exit handler for job with pid {pid}")
}
}
EventDescription::CallerExit { .. } => {

View File

@@ -36,8 +36,8 @@
use crate::parser::{Block, BlockId, BlockType, EvalRes, Parser};
use crate::proc::{
hup_jobs, is_interactive_session, jobs_requiring_warning_on_exit, no_exec,
print_exit_warning_for_jobs, InternalProc, Job, JobGroupRef, Pid, ProcStatus, Process,
ProcessType, TtyTransfer,
print_exit_warning_for_jobs, InternalProc, Job, JobGroupRef, ProcStatus, Process, ProcessType,
TtyTransfer, INVALID_PID,
};
use crate::reader::{reader_run_count, restore_term_mode};
use crate::redirection::{dup2_list_resolve_chain, Dup2List};
@@ -56,7 +56,6 @@
use nix::sys::stat;
use std::ffi::CStr;
use std::io::{Read, Write};
use std::num::NonZeroU32;
use std::os::fd::{AsRawFd, OwnedFd, RawFd};
use std::slice;
use std::sync::atomic::Ordering;
@@ -72,6 +71,10 @@ pub fn exec_job(parser: &Parser, job: &Job, block_io: IoChain) -> bool {
return true;
}
if job.entitled_to_terminal() {
crate::input_common::terminal_protocols_disable_ifn();
}
// Handle an exec call.
if job.processes()[0].typ == ProcessType::exec {
// If we are interactive, perhaps disallow exec if there are background jobs.
@@ -488,7 +491,7 @@ fn internal_exec(vars: &EnvStack, j: &Job, block_io: IoChain) {
// child_setup_process makes sure signals are properly set up.
let redirs = dup2_list_resolve_chain(&all_ios);
if child_setup_process(
None, /* not claim_tty */
0, /* not claim_tty */
blocked_signals,
false, /* not is_forked */
&redirs,
@@ -674,10 +677,9 @@ fn fork_child_for_process(
) -> LaunchResult {
// Claim the tty from fish, if the job wants it and we are the pgroup leader.
let claim_tty_from = if p.leads_pgrp && job.group().wants_terminal() {
// getpgrp(2) cannot fail and always returns the (positive) caller's pgid
Some(NonZeroU32::new(unsafe { libc::getpgrp() } as u32).unwrap())
unsafe { libc::getpgrp() }
} else {
None
INVALID_PID
};
// Decide if the job wants to set a custom sigmask.
@@ -702,24 +704,22 @@ fn fork_child_for_process(
// Record the pgroup if this is the leader.
// Both parent and child attempt to send the process to its new group, to resolve the race.
let pid = if is_parent {
p.set_pid(if is_parent {
pid
} else {
unsafe { libc::getpid() }
};
p.set_pid(pid);
});
if p.leads_pgrp {
job.group().set_pgid(pid);
}
{
let pid = p.pid().unwrap();
if let Some(pgid) = job.group().get_pgid() {
let err = execute_setpgid(pid, pgid, is_parent);
let err = execute_setpgid(p.pid(), pgid, is_parent);
if err != 0 {
report_setpgid_error(
err,
is_parent,
pid,
p.pid(),
pgid,
job.job_id().as_num(),
&narrow_cmd,
@@ -795,6 +795,7 @@ fn create_output_stream_for_builtin(
/// Handle output from a builtin, by printing the contents of builtin_io_streams to the redirections
/// given in io_chain.
fn handle_builtin_output(
parser: &Parser,
j: &Job,
@@ -866,7 +867,7 @@ fn exec_external_command(
return Err(());
}
};
let pid = Pid::new(pid).expect("Should have either a valid pid, or an error");
assert!(pid > 0, "Should have either a valid pid, or an error");
// This usleep can be used to test for various race conditions
// (https://github.com/fish-shell/fish-shell/issues/360).
@@ -884,9 +885,9 @@ fn exec_external_command(
);
// these are all things do_fork() takes care of normally (for forked processes):
p.pid.store(pid);
p.pid.store(pid, Ordering::Relaxed);
if p.leads_pgrp {
j.group().set_pgid(pid.as_pid_t());
j.group().set_pgid(pid);
// posix_spawn should in principle set the pgid before returning.
// In glibc, posix_spawn uses fork() and the pgid group is set on the child side;
// therefore the parent may not have seen it be set yet.
@@ -1323,7 +1324,7 @@ fn exec_process_in_job(
exec_external_command(parser, j, p, &process_net_io_chain)?;
// It's possible (though unlikely) that this is a background process which recycled a
// pid from another, previous background process. Forget any such old process.
parser.mut_wait_handles().remove_by_pid(p.pid().unwrap());
parser.mut_wait_handles().remove_by_pid(p.pid());
Ok(())
}
ProcessType::exec => {

View File

@@ -89,11 +89,6 @@ pub fn fish_wcwidth(c: char) -> isize {
/// fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if
/// the system one is busted.
pub fn fish_wcswidth(s: &wstr) -> isize {
// ascii fast path; empty iterator returns true for .all()
if s.chars().all(|c| c.is_ascii() && !c.is_ascii_control()) {
return s.len() as isize;
}
let mut result = 0;
for c in s.chars() {
let w = fish_wcwidth(c);
@@ -131,7 +126,6 @@ pub fn fish_mkstemp_cloexec(name_template: CString) -> Result<(File, CString), E
}
}
/// Compare two wide strings in a case-insensitive fashion
pub fn wcscasecmp(lhs: &wstr, rhs: &wstr) -> cmp::Ordering {
use std::char::ToLowercase;
use widestring::utfstr::CharsUtf32;
@@ -155,20 +149,18 @@ fn next(&mut self) -> Option<Self::Item> {
}
self.current = self.chars.next()?.to_lowercase();
self.current.next()
self.next()
}
}
impl<'a> ToLowerBuffer<'a> {
pub fn from(w: &'a wstr) -> Self {
let mut empty = 'a'.to_lowercase();
let _ = empty.next();
debug_assert!(empty.next().is_none());
let mut chars = w.chars();
Self {
current: chars.next().map(|c| c.to_lowercase()).unwrap_or_else(|| {
let mut empty = 'a'.to_lowercase();
let _ = empty.next();
debug_assert!(empty.next().is_none());
empty
}),
current: chars.next().map(|c| c.to_lowercase()).unwrap_or(empty),
chars,
}
}

View File

@@ -3,13 +3,11 @@
// That means no locking, no allocating, no freeing memory, etc!
use super::flog_safe::FLOG_SAFE;
use crate::nix::getpid;
use crate::proc::Pid;
use crate::redirection::Dup2List;
use crate::signal::signal_reset_handlers;
use crate::{common::exit_without_destructors, wutil::fstat};
use libc::{c_char, pid_t, O_RDONLY};
use std::ffi::CStr;
use std::num::NonZeroU32;
use std::os::unix::fs::MetadataExt;
use std::time::Duration;
@@ -53,20 +51,20 @@ fn strlen_safe(s: *const libc::c_char) -> usize {
pub(crate) fn report_setpgid_error(
err: i32,
is_parent: bool,
pid: Pid,
desired_pgid: Pid,
pid: pid_t,
desired_pgid: pid_t,
job_id: i64,
command: &CStr,
argv0: &CStr,
) {
let cur_group = unsafe { libc::getpgid(pid.as_pid_t()) };
let cur_group = unsafe { libc::getpgid(pid) };
FLOG_SAFE!(
warning,
"Could not send ",
if is_parent { "child" } else { "self" },
" ",
pid.get(),
pid,
", '",
argv0,
"' in job ",
@@ -76,35 +74,35 @@ pub(crate) fn report_setpgid_error(
"' from group ",
cur_group,
" to group ",
desired_pgid.get(),
desired_pgid,
);
match err {
libc::EACCES => FLOG_SAFE!(error, "setpgid: Process ", pid.get(), " has already exec'd"),
libc::EACCES => FLOG_SAFE!(error, "setpgid: Process ", pid, " has already exec'd"),
libc::EINVAL => FLOG_SAFE!(error, "setpgid: pgid ", cur_group, " unsupported"),
libc::EPERM => {
FLOG_SAFE!(
error,
"setpgid: Process ",
pid.get(),
pid,
" is a session leader or pgid ",
cur_group,
" does not match"
);
}
libc::ESRCH => FLOG_SAFE!(error, "setpgid: Process ID ", pid.get(), " does not match"),
libc::ESRCH => FLOG_SAFE!(error, "setpgid: Process ID ", pid, " does not match"),
_ => FLOG_SAFE!(error, "setpgid: Unknown error number ", err),
}
}
/// Execute setpgid, moving pid into the given pgroup.
/// Return the result of setpgid.
pub fn execute_setpgid(pid: Pid, pgroup: Pid, is_parent: bool) -> i32 {
pub fn execute_setpgid(pid: pid_t, pgroup: pid_t, is_parent: bool) -> i32 {
// There is a comment "Historically we have looped here to support WSL."
// TODO: stop looping.
let mut eperm_count = 0;
loop {
if unsafe { libc::setpgid(pid.as_pid_t(), pgroup.as_pid_t()) } == 0 {
if unsafe { libc::setpgid(pid, pgroup) } == 0 {
return 0;
}
let err = errno::errno().0;
@@ -145,7 +143,7 @@ pub fn execute_setpgid(pid: Pid, pgroup: Pid, is_parent: bool) -> i32 {
/// Set up redirections and signal handling in the child process.
pub fn child_setup_process(
claim_tty_from: Option<NonZeroU32>,
claim_tty_from: pid_t,
sigmask: Option<&libc::sigset_t>,
is_forked: bool,
dup2s: &Dup2List,
@@ -175,10 +173,7 @@ pub fn child_setup_process(
return err;
}
}
if claim_tty_from
// tcgetpgrp() can return -1 but pid.get() cannot, so cast the latter to the former
.is_some_and(|pid| unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) } == pid.get() as i32)
{
if claim_tty_from >= 0 && unsafe { libc::tcgetpgrp(libc::STDIN_FILENO) } == claim_tty_from {
// Assign the terminal within the child to avoid the well-known race between tcsetgrp() in
// the parent and the child executing. We are not interested in error handling here, except
// we try to avoid this for non-terminals; in particular pipelines often make non-terminal

View File

@@ -3,7 +3,7 @@
use super::blocked_signals_for_job;
use crate::proc::Job;
use crate::redirection::Dup2List;
use crate::signal::signals_to_default;
use crate::signal::get_signals_with_handlers;
use crate::{exec::is_thompson_shell_script, libc::_PATH_BSHELL};
use errno::Errno;
use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
@@ -105,17 +105,27 @@ pub fn new(j: &Job, dup2s: &Dup2List) -> Result<PosixSpawner, Errno> {
// desired_pgid tracks the pgroup for the process. If it is none, the pgroup is left unchanged.
// If it is zero, create a new pgroup from the pid. If it is >0, join that pgroup.
let desired_pgid = if let Some(pgid) = j.get_pgid() {
Some(pgid.get())
Some(pgid)
} else if j.processes()[0].leads_pgrp {
Some(0)
} else {
None
};
// Set the handling for job control signals back to the default.
let reset_signal_handlers = true;
// Remove all signal blocks.
let reset_sigmask = true;
// Set our flags.
let mut flags: i32 = 0;
flags |= libc::POSIX_SPAWN_SETSIGDEF;
flags |= libc::POSIX_SPAWN_SETSIGMASK;
if reset_signal_handlers {
flags |= libc::POSIX_SPAWN_SETSIGDEF;
}
if reset_sigmask {
flags |= libc::POSIX_SPAWN_SETSIGMASK;
}
if desired_pgid.is_some() {
flags |= libc::POSIX_SPAWN_SETPGROUP;
}
@@ -126,13 +136,19 @@ pub fn new(j: &Job, dup2s: &Dup2List) -> Result<PosixSpawner, Errno> {
}
// Everybody gets default handlers.
attr.set_sigdefault(&signals_to_default)?;
if reset_signal_handlers {
let mut sigdefault: libc::sigset_t = unsafe { std::mem::zeroed() };
get_signals_with_handlers(&mut sigdefault);
attr.set_sigdefault(&sigdefault)?;
}
// Reset the sigmask.
let mut sigmask = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut sigmask) };
blocked_signals_for_job(j, &mut sigmask);
attr.set_sigmask(&sigmask)?;
// Potentially reset the sigmask.
if reset_sigmask {
let mut sigmask = unsafe { std::mem::zeroed() };
unsafe { libc::sigemptyset(&mut sigmask) };
blocked_signals_for_job(j, &mut sigmask);
attr.set_sigmask(&sigmask)?;
}
// Apply our dup2s.
for act in dup2s.get_actions() {

View File

@@ -241,8 +241,7 @@ pub fn exists_no_autoload(cmd: &wstr) -> bool {
}
let mut funcset = FUNCTION_SET.lock().unwrap();
// Check if we either have the function, or it could be autoloaded.
let tombstoned = funcset.autoload_tombstones.contains(cmd);
funcset.funcs.contains_key(cmd) || (!tombstoned && funcset.autoloader.can_autoload(cmd))
funcset.get_props(cmd).is_some() || funcset.autoloader.can_autoload(cmd)
}
/// Remove the function with the specified name.
@@ -443,11 +442,9 @@ pub fn annotated_definition(&self, name: &wstr) -> WString {
sprintf!(=> &mut out, " --on-variable %ls", name);
}
EventDescription::ProcessExit { pid } => {
let pid = pid.map(|p| p.get()).unwrap_or(0);
sprintf!(=> &mut out, " --on-process-exit %d", pid)
sprintf!(=> &mut out, " --on-process-exit %d", pid);
}
EventDescription::JobExit { pid, .. } => {
let pid = pid.map(|p| p.get()).unwrap_or(0);
sprintf!(=> &mut out, " --on-job-exit %d", pid);
}
EventDescription::CallerExit { .. } => {

View File

@@ -15,13 +15,6 @@
//!
//! 5. The chaos_mode boolean can be set to true to do things like lower buffer sizes which can
//! trigger race conditions. This is useful for testing.
//!
//! Locking on remote filesystems may hang for an unacceptably long time. For that reason, fish
//! does not take locks on the file if it believes the history file is on a remote filesystem,
//! or if the mmap fails with ENODEV, or if the first lock attempt takes excessively long.
//! Eliding locks means that two concurrent shell sessions with a remote history file may, in
//! rare cases with multiple simultaneous shell sessions, lose a history item; this is
//! considered preferable to hanging the the shell waiting for a lock.
use crate::{
common::cstr2wcstring, env::EnvVar, wcstringutil::trim,
@@ -1436,8 +1429,6 @@ fn format_history_record(
) -> WString {
let mut result = WString::new();
let seconds = time_to_seconds(item.timestamp());
// This warns for musl, but the warning is useless to us - there is nothing we can or should do.
#[allow(deprecated)]
let seconds = seconds as libc::time_t;
let mut timestamp: libc::tm = unsafe { std::mem::zeroed() };
if let Some(show_time_format) = show_time_format.and_then(|s| CString::new(s).ok()) {

View File

@@ -130,15 +130,7 @@ pub fn create(file: &mut File) -> Option<Self> {
let region = if should_mmap() {
match MmapRegion::map_file(file.as_raw_fd(), len) {
Ok(region) => region,
Err(err) if err.raw_os_error() == Some(ENODEV) => {
// Our mmap failed with ENODEV, which means the underlying
// filesystem does not support mapping. Treat this as a hint
// that the filesystem is remote, and so disable locks for
// the history file.
super::ABANDONED_LOCKING.store(true);
// Create an anonymous mapping and read() the file into it.
map_anon(file, len)?
}
Err(err) if err.raw_os_error() == Some(ENODEV) => map_anon(file, len)?,
Err(_err) => return None,
}
} else {
@@ -273,9 +265,9 @@ fn maybe_unescape_yaml_fish_2_0(s: &[u8]) -> Cow<[u8]> {
unescape_yaml_fish_2_0(s).into()
}
// Unescapes the fish-specific yaml variant. Use [`maybe_unescape_yaml_fish_2_0()`] if you're not
// positive the input contains an escape.
//
/// Unescapes the fish-specific yaml variant. Use [`maybe_unescape_yaml_fish_2_0()`] if you're not
/// positive the input contains an escape.
// This function is called on every input event and shows up heavily in all flamegraphs.
// Various approaches were benchmarked against real-world fish-history files on lines with escapes,
// and this implementation (chunk_loop_box) won out. Make changes with care!

View File

@@ -431,13 +431,13 @@ pub fn update_wait_on_sequence_key_ms(vars: &EnvStack) {
static TERMINAL_PROTOCOLS: AtomicBool = AtomicBool::new(false);
static IS_TMUX: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
pub static IN_MIDNIGHT_COMMANDER_PRE_CSI_U: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
static IN_MIDNIGHT_COMMANDER: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
static IN_ITERM_PRE_CSI_U: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
static IN_JETBRAINS: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
pub fn terminal_protocol_hacks() {
use std::env::var_os;
IS_TMUX.store(var_os("TMUX").is_some());
IN_MIDNIGHT_COMMANDER.store(var_os("MC_TMPDIR").is_some());
IN_ITERM_PRE_CSI_U.store(
var_os("LC_TERMINAL").is_some_and(|term| term.as_os_str().as_bytes() == b"iTerm2")
&& var_os("LC_TERMINAL_VERSION").is_some_and(|version| {
@@ -448,10 +448,6 @@ pub fn terminal_protocol_hacks() {
version < (3, 5, 6)
}),
);
IN_JETBRAINS.store(
var_os("TERMINAL_EMULATOR")
.is_some_and(|term| term.as_os_str().as_bytes() == b"JetBrains-JediTerm"),
);
}
fn parse_version(version: &wstr) -> Option<(i64, i64, i64)> {
@@ -478,13 +474,10 @@ pub fn terminal_protocols_enable_ifn() {
return;
}
TERMINAL_PROTOCOLS.store(true, Ordering::Release);
let sequences = if IN_MIDNIGHT_COMMANDER_PRE_CSI_U.load() {
let sequences = if IN_MIDNIGHT_COMMANDER.load() {
"\x1b[?2004h"
} else if IN_ITERM_PRE_CSI_U.load() {
concat!("\x1b[?2004h", "\x1b[>4;1m", "\x1b[>5u", "\x1b=",)
} else if IN_JETBRAINS.load() {
// Jetbrains IDE terminals vomit CSI u
concat!("\x1b[?2004h", "\x1b[>4;1m", "\x1b=",)
} else {
concat!(
"\x1b[?2004h", // Bracketed paste
@@ -507,8 +500,6 @@ pub(crate) fn terminal_protocols_disable_ifn() {
}
let sequences = if IN_ITERM_PRE_CSI_U.load() {
concat!("\x1b[?2004l", "\x1b[>4;0m", "\x1b[<1u", "\x1b>",)
} else if IN_JETBRAINS.load() {
concat!("\x1b[?2004l", "\x1b[>4;0m", "\x1b>",)
} else {
concat!(
"\x1b[?2004l", // Bracketed paste
@@ -593,44 +584,28 @@ fn try_pop(&mut self) -> Option<CharEvent> {
self.get_input_data_mut().queue.pop_front()
}
/// An "infallible" version of [`try_readch`](Self::try_readch) to be used when the input pipe
/// fd is expected to outlive the input reader. Will panic upon EOF.
#[inline(always)]
/// Function used by input_readch to read bytes from stdin until enough bytes have been read to
/// convert them to a wchar_t. Conversion is done using mbrtowc. If a character has previously
/// been read and then 'unread' using \c input_common_unreadch, that character is returned.
fn readch(&mut self) -> CharEvent {
match self.try_readch(/*blocking*/ true) {
Some(c) => c,
None => unreachable!(),
}
}
/// Function used by [`input_readch`] to read bytes from stdin until enough bytes have been read to
/// convert them to a wchar_t. Conversion is done using `mbrtowc`. If a character has previously
/// been read and then 'unread', that character is returned.
///
/// This is guaranteed to keep returning `Some(CharEvent)` so long as the input stream remains
/// open; `None` is only returned upon EOF as the main loop within blocks until input becomes
/// available.
///
/// This method is used directly by the fuzzing harness to avoid a panic on bounded inputs.
fn try_readch(&mut self, blocking: bool) -> Option<CharEvent> {
loop {
// Do we have something enqueued already?
// Note this may be initially true, or it may become true through calls to
// iothread_service_main() or env_universal_barrier() below.
if let Some(mevt) = self.try_pop() {
return Some(mevt);
return mevt;
}
// We are going to block; but first allow any override to inject events.
self.prepare_to_select();
if let Some(mevt) = self.try_pop() {
return Some(mevt);
return mevt;
}
let rr = readb(self.get_in_fd(), blocking);
let rr = readb(self.get_in_fd(), /*blocking=*/ true);
match rr {
ReadbResult::Eof => {
return Some(CharEvent::Eof);
return CharEvent::Eof;
}
ReadbResult::Interrupted => {
@@ -698,16 +673,16 @@ fn try_readch(&mut self, blocking: bool) -> Option<CharEvent> {
continue;
}
return if let Some(key) = key {
Some(CharEvent::from_key_seq(key, seq))
CharEvent::from_key_seq(key, seq)
} else {
self.insert_front(seq.chars().skip(1).map(CharEvent::from_char));
let Some(c) = seq.chars().next() else {
continue;
};
Some(CharEvent::from_key_seq(Key::from_raw(c), seq))
CharEvent::from_key_seq(Key::from_raw(c), seq)
};
}
ReadbResult::NothingToRead => return None,
ReadbResult::NothingToRead => unreachable!(),
}
}
}
@@ -831,8 +806,7 @@ fn parse_codepoint(
fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<Key> {
let mut next_char = |zelf: &mut Self| zelf.try_readb(buffer).unwrap_or(0xff);
// The maximum number of CSI parameters is defined by NPAR, nominally 16.
let mut params = [[0_u32; 4]; 16];
let mut params = [[0_u32; 16]; 4];
let mut c = next_char(self);
let private_mode;
if matches!(c, b'?' | b'<' | b'=' | b'>') {
@@ -846,17 +820,13 @@ fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<Key> {
let mut subcount = 0;
while count < 16 && c >= 0x30 && c <= 0x3f {
if c.is_ascii_digit() {
// Return None on invalid ascii numeric CSI parameter exceeding u32 bounds
params[count][subcount] = params[count][subcount]
.checked_mul(10)
.and_then(|result| result.checked_add(u32::from(c - b'0')))?;
params[count][subcount] = params[count][subcount] * 10 + u32::from(c - b'0');
} else if c == b':' && subcount < 3 {
subcount += 1;
} else if c == b';' {
count += 1;
subcount = 0;
} else {
// Unexpected character or unrecognized CSI
return None;
}
c = next_char(self);
@@ -930,7 +900,7 @@ fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<Key> {
b'T' => {
self.disable_mouse_tracking();
// VT200 button released in mouse highlighting mode past end-of-line. 9 characters.
for _ in 0..6 {
for _ in 0..7 {
let _ = next_char(self);
}
return None;

View File

@@ -1,5 +1,5 @@
use crate::global_safety::RelaxedAtomicBool;
use crate::proc::{AtomicPid, JobGroupRef, Pid};
use crate::proc::JobGroupRef;
use crate::signal::Signal;
use crate::wchar::prelude::*;
use std::cell::RefCell;
@@ -12,7 +12,7 @@
#[repr(transparent)]
pub struct JobId(NonZeroU32);
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MaybeJobId(pub Option<JobId>);
impl std::ops::Deref for MaybeJobId {
@@ -72,8 +72,8 @@ pub struct JobGroup {
/// Whether we are in the foreground, meaning the user is waiting for this job to complete.
pub is_foreground: RelaxedAtomicBool,
/// The pgid leading our group. This is only ever set if [`job_control`](Self::JobControl) is
/// true. We ensure the value (when set) is always non-negative and non-zero.
pgid: AtomicPid,
/// true. We ensure the value (when set) is always non-negative.
pgid: RefCell<Option<libc::pid_t>>,
/// The original command which produced this job tree.
pub command: WString,
/// Our job id, if any. `None` here should evaluate to `-1` for ffi purposes.
@@ -149,14 +149,14 @@ pub fn set_pgid(&self, pgid: libc::pid_t) {
"Should not set a pgid for a group that doesn't want job control!"
);
assert!(pgid >= 0, "Invalid pgid!");
assert!(self.pgid.borrow().is_none(), "JobGroup::pgid already set!");
let old_pgid = self.pgid.swap(Pid::new(pgid).unwrap());
assert!(old_pgid.is_none(), "JobGroup::pgid already set!");
self.pgid.replace(Some(pgid));
}
/// Returns the value of [`JobGroup::pgid`]. This is never fish's own pgid!
pub fn get_pgid(&self) -> Option<Pid> {
self.pgid.load()
pub fn get_pgid(&self) -> Option<libc::pid_t> {
*self.pgid.borrow()
}
}
@@ -223,7 +223,7 @@ pub fn new(command: WString, id: MaybeJobId, job_control: bool, wants_term: bool
tmodes: RefCell::default(),
signal: 0.into(),
is_foreground: RelaxedAtomicBool::new(false),
pgid: AtomicPid::default(),
pgid: RefCell::default(),
}
}

Some files were not shown because too many files have changed in this diff Show More