mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-10 09:41:16 -03:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67b6afffd4 | ||
|
|
6df88e1a9f | ||
|
|
61884bda36 | ||
|
|
66584dadcc | ||
|
|
5944518e6e | ||
|
|
19502ff9e7 | ||
|
|
e37e1b8f78 | ||
|
|
5d31be1c3e | ||
|
|
7a959723ef | ||
|
|
ad7631093d | ||
|
|
bd8d268255 | ||
|
|
83f29ed09c | ||
|
|
aca6836103 | ||
|
|
c9f1979b05 | ||
|
|
4a35c48ce5 | ||
|
|
f336cf5624 | ||
|
|
1504731d53 | ||
|
|
9dce68fab4 | ||
|
|
815bc054e7 | ||
|
|
b8af4b20c2 | ||
|
|
c06830ccf2 | ||
|
|
f96b9c53ce | ||
|
|
12527d1522 | ||
|
|
46422b6a16 | ||
|
|
97f0809b62 | ||
|
|
d30a2c5240 | ||
|
|
7ee6d91ba0 | ||
|
|
a94c4e96ab | ||
|
|
e767bb623f | ||
|
|
e925eccad2 | ||
|
|
200eeffeee | ||
|
|
0d453039ac | ||
|
|
044afefc5c | ||
|
|
5cce0918a9 | ||
|
|
3a673aff63 | ||
|
|
880aa479bf | ||
|
|
303af078f3 | ||
|
|
1e069b0fff |
18
.github/workflows/main.yml
vendored
18
.github/workflows/main.yml
vendored
@@ -28,10 +28,10 @@ jobs:
|
||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
- name: make
|
||||
run: |
|
||||
make VERBOSE=1
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
run: |
|
||||
make VERBOSE=1 test
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
|
||||
ubuntu-32bit-static-pcre2:
|
||||
|
||||
@@ -54,10 +54,10 @@ jobs:
|
||||
cmake -DFISH_USE_SYSTEM_PCRE2=OFF -DRust_CARGO_TARGET=i586-unknown-linux-gnu ..
|
||||
- name: make
|
||||
run: |
|
||||
make VERBOSE=1
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
run: |
|
||||
make VERBOSE=1 test
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
|
||||
ubuntu-asan:
|
||||
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
cmake .. -DASAN=1 -DRust_CARGO_TARGET=x86_64-unknown-linux-gnu -DCMAKE_BUILD_TYPE=Debug
|
||||
- name: make
|
||||
run: |
|
||||
make VERBOSE=1
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
env:
|
||||
FISH_CI_SAN: 1
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
llvm_version=$(clang --version | awk 'NR==1 { split($NF, version, "."); print version[1] }')
|
||||
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-$llvm_version
|
||||
export LSAN_OPTIONS="$LSAN_OPTIONS:suppressions=$PWD/build_tools/lsan_suppressions.txt"
|
||||
make VERBOSE=1 test
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
|
||||
# Our clang++ tsan builds are not recognizing safe rust patterns (such as the fact that Drop
|
||||
# cannot be called while a thread is using the object in question). Rust has its own way of
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
# make
|
||||
# - name: make fish_run_tests
|
||||
# run: |
|
||||
# make fish_run_tests
|
||||
# make -C build fish_run_tests
|
||||
|
||||
macos:
|
||||
|
||||
@@ -160,7 +160,7 @@ jobs:
|
||||
cmake -DWITH_GETTEXT=NO -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- name: make
|
||||
run: |
|
||||
make VERBOSE=1
|
||||
make -C build VERBOSE=1
|
||||
- name: make fish_run_tests
|
||||
run: |
|
||||
make VERBOSE=1 test
|
||||
make -C build VERBOSE=1 fish_run_tests
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
fish 4.0.1 (released March 12, 2025)
|
||||
====================================
|
||||
|
||||
This release of fish includes the following improvements compared to fish 4.0.0:
|
||||
|
||||
- Key combinations using the super (Windows/command) key can be bound using the :kbd:`super-` prefix (:issue:`11217`).
|
||||
- Konsole's menu shows the "Open folder with" option again (:issue:`11198`).
|
||||
- ``$fish_color_search_match`` will now only be applied to the foreground color if it has an explicit foreground. For example, this allows setting::
|
||||
set -g fish_color_search_match --reverse
|
||||
- Cursor shape commands (``\e[2 q``) are no longer sent in non-interactive shells or in redirections (:issue:`11255`).
|
||||
- :doc:`status <cmds/status>` gained a ``is-interactive-read`` subcommand, to check whether the script is being called from an interactive :doc:`read <cmds/read>` invocation.
|
||||
- fish's background tasks are now started in a way that avoids an error on macOS Terminal.app (:issue:`11181`).
|
||||
- Using key combinations within qemu should work correctly.
|
||||
- Prompts containing control characters no longer cause incorrect display of command lines (:issue:`11252`).
|
||||
- Cancelling the command-line in Vi mode displays correctly again (:issue:`11261`).
|
||||
- The acidhub prompt properly displays the git branch again (:issue:`11179`).
|
||||
- Completions for ``wine`` correctly include files again (:issue:`11202`).
|
||||
- On macOS, paths from ``/etc/paths`` and ``/etc/manpaths`` containing colons are handled correctly (:issue:`10684`). This functionality was included in the 4.0.0 release notes but was missing from the source code.
|
||||
- The XTerm ``modifyOtherKeys`` keyboard encoding is no longer used under WezTerm, as it does not work correctly in all layouts (:issue:`11204`).
|
||||
- kbd:`option-left` and other similar keys should now work in iTerm versions before 3.5.12; the kitty keyboard protocol is now disabled on these versions (:issue:`11192`).
|
||||
- The kitty keyboard protocol is no longer used under Midnight Commander, as it does not work correctly (:issue:`10640`).
|
||||
- fish now sends the commandline along with the OSC 133 semantic prompt command start sequence. This fixes a test in the kitty terminal (:issue:`11203`).
|
||||
- Git completions for third-party commands like "git-absorb" are completed correctly again (:issue:`11205`).
|
||||
- Completions for ``diskutil`` no longer produce an error (:issue:`11201`).
|
||||
- The output of certain error messages no longer prints newlines to standard output (:issue:`11248`).
|
||||
- A number of crashes have been fixed, including file names longer than 255 bytes (:issue:`11221`), using fish on a btrfs filesystem (:issue:`11219`), history files that do not have the expected format (:issue:`11236`), and pasting into an empty command line (:issue:`11256`).
|
||||
|
||||
--------------
|
||||
|
||||
fish 4.0.0 (released February 27, 2025)
|
||||
=======================================
|
||||
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -112,7 +112,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "4.0.0-beta.1"
|
||||
version = "4.0.1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
|
||||
@@ -16,7 +16,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.0.0-beta.1"
|
||||
version = "4.0.1"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
default-run = "fish"
|
||||
|
||||
@@ -306,18 +306,23 @@ if (Rust_RESOLVE_RUSTUP_TOOLCHAINS)
|
||||
set(_DISCOVERED_TOOLCHAINS_VERSION "")
|
||||
|
||||
foreach(_TOOLCHAIN_RAW ${_TOOLCHAINS_RAW})
|
||||
if (_TOOLCHAIN_RAW MATCHES "([a-zA-Z0-9\\._\\-]+)[ \t\r\n]?(\\(default\\) \\(override\\)|\\(default\\)|\\(override\\))?[ \t\r\n]+(.+)")
|
||||
# We're going to try to parse the output of `rustup toolchain list --verbose`.
|
||||
# We expect output like this:
|
||||
# stable-random-toolchain-junk (parenthesized-random-stuff-like-active-or-default) /path/to/toolchain/blah/more-blah
|
||||
# In the following regex, we capture the toolchain name, any parenthesized stuff, and then the path.
|
||||
message(STATUS "Parsing toolchain: ${_TOOLCHAIN_RAW}")
|
||||
if (_TOOLCHAIN_RAW MATCHES "([^\t ]+)[\t ]*(\\(.*\\))?[\t ]*(.+)")
|
||||
set(_TOOLCHAIN "${CMAKE_MATCH_1}")
|
||||
set(_TOOLCHAIN_TYPE "${CMAKE_MATCH_2}")
|
||||
|
||||
set(_TOOLCHAIN_PATH "${CMAKE_MATCH_3}")
|
||||
set(_TOOLCHAIN_${_TOOLCHAIN}_PATH "${CMAKE_MATCH_3}")
|
||||
|
||||
if (_TOOLCHAIN_TYPE MATCHES ".*\\(default\\).*")
|
||||
if (_TOOLCHAIN_TYPE MATCHES "default")
|
||||
set(_TOOLCHAIN_DEFAULT "${_TOOLCHAIN}")
|
||||
endif()
|
||||
|
||||
if (_TOOLCHAIN_TYPE MATCHES ".*\\(override\\).*")
|
||||
if (_TOOLCHAIN_TYPE MATCHES "override")
|
||||
set(_TOOLCHAIN_OVERRIDE "${_TOOLCHAIN}")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -26,15 +26,12 @@ The first argument to fish_title contains the most recently executed foreground
|
||||
|
||||
This requires that your terminal supports programmable titles and the feature is turned on.
|
||||
|
||||
To disable setting the title, use an empty function (see below).
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
A simple title:
|
||||
|
||||
|
||||
|
||||
::
|
||||
A simple title::
|
||||
|
||||
function fish_title
|
||||
set -q argv[1]; or set argv fish
|
||||
@@ -43,3 +40,7 @@ A simple title:
|
||||
echo (fish_prompt_pwd_dir_length=1 prompt_pwd): $argv;
|
||||
end
|
||||
|
||||
Do not change the title::
|
||||
|
||||
function fish_title
|
||||
end
|
||||
|
||||
@@ -11,6 +11,7 @@ Synopsis
|
||||
status
|
||||
status is-login
|
||||
status is-interactive
|
||||
status is-interactive-read
|
||||
status is-block
|
||||
status is-breakpoint
|
||||
status is-command-substitution
|
||||
@@ -50,6 +51,9 @@ The following operations (subcommands) are available:
|
||||
**is-interactive**, **-i** or **--is-interactive**
|
||||
Returns 0 if fish is interactive - that is, connected to a keyboard.
|
||||
|
||||
**is-interactive-read** or **--is-interactive-read**
|
||||
Returns 0 if fish is running an interactive :doc:`read <read>` builtin which is connected to a keyboard.
|
||||
|
||||
**is-login**, **-l** or **--is-login**
|
||||
Returns 0 if fish is a login shell - that is, if fish should perform login tasks such as setting up :envvar:`PATH`.
|
||||
|
||||
|
||||
@@ -53,7 +53,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_mounted_volumes ; __fish_diskutil_devices)'
|
||||
|
||||
# mount
|
||||
complete -f -c diskutil -n __fish_use_subcommand -a mount -d 'Mount a single volume'
|
||||
|
||||
@@ -2569,11 +2569,11 @@ complete -c git -n __fish_git_needs_command -a '(__fish_git_custom_commands)' -d
|
||||
function __fish_git_complete_custom_command -a subcommand
|
||||
set -l cmd (commandline -xpc)
|
||||
set -e cmd[1] # Drop "git".
|
||||
set -lx subcommand_args
|
||||
set -l subcommand_args
|
||||
if argparse -s (__fish_git_global_optspecs) -- $cmd
|
||||
set subcommand_args $argv[2..] # Drop the subcommand.
|
||||
end
|
||||
complete -C "git-$subcommand \$subcommand_args "(commandline -ct)
|
||||
complete -C "git-$subcommand $(string escape -- $subcommand_args) "(commandline -ct)
|
||||
end
|
||||
|
||||
# source git-* commands' autocompletion file if exists
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# Note that when a completion file is sourced a new block scope is created so `set -l` works.
|
||||
set -l __fish_status_all_commands basename current-command current-commandline current-filename current-function current-line-number dirname features filename fish-path function is-block is-breakpoint is-command-substitution is-full-job-control is-interactive is-interactive-job-control is-login is-no-job-control job-control line-number print-stack-trace stack-trace test-feature
|
||||
set -l __fish_status_all_commands \
|
||||
basename \
|
||||
buildinfo \
|
||||
current-command \
|
||||
current-commandline \
|
||||
current-filename \
|
||||
current-function \
|
||||
current-line-number \
|
||||
dirname \
|
||||
features \
|
||||
filename \
|
||||
fish-path \
|
||||
function \
|
||||
is-block \
|
||||
is-breakpoint \
|
||||
is-command-substitution \
|
||||
is-full-job-control \
|
||||
is-interactive \
|
||||
is-interactive-job-control \
|
||||
is-interactive-read \
|
||||
is-login \
|
||||
is-no-job-control \
|
||||
job-control \
|
||||
line-number \
|
||||
print-stack-trace \
|
||||
stack-trace \
|
||||
test-feature
|
||||
|
||||
# These are the recognized flags.
|
||||
complete -c status -s h -l help -d "Display help and exit"
|
||||
@@ -7,6 +33,7 @@ complete -c status -s h -l help -d "Display help and exit"
|
||||
# The "is-something" subcommands.
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-login -d "Test if this is a login shell"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-interactive -d "Test if this is an interactive shell"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-interactive-read -d "Test if inside an interactive read builtin"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-command-substitution -d "Test if a command substitution is currently evaluated"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-block -d "Test if a code block is currently evaluated"
|
||||
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-breakpoint -d "Test if a breakpoint is currently in effect"
|
||||
|
||||
@@ -325,7 +325,6 @@ function __fish_wine__complete_winepath_subcommand --argument-names command
|
||||
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'
|
||||
|
||||
@@ -166,13 +166,11 @@ if status --is-login
|
||||
|
||||
# Populate path according to config files
|
||||
for path_file in $argv[2] $argv[3]/*
|
||||
if test -f $path_file
|
||||
while read -l entry
|
||||
if not contains -- $entry $result
|
||||
test -n "$entry"
|
||||
and set -a result $entry
|
||||
end
|
||||
end <$path_file
|
||||
for entry in (string split : <? $path_file)
|
||||
if not contains -- $entry $result
|
||||
test -n "$entry"
|
||||
and set -a result $entry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -40,8 +40,7 @@ if test $status -eq 0 -a (count $sysver) -eq 3
|
||||
|
||||
if test $age -ge $max_age
|
||||
test -d "$dir" || mkdir -m 700 -p $dir
|
||||
/usr/libexec/makewhatis -o "$whatis" (/usr/bin/manpath | string split : | xargs realpath) >/dev/null 2>&1 </dev/null &
|
||||
disown $last_pid
|
||||
/bin/sh -c '( "$@" ) >/dev/null 2>&1 </dev/null &' -- /usr/libexec/makewhatis -o "$whatis" (/usr/bin/manpath | string split : | xargs realpath)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -99,9 +99,8 @@ end" >$__fish_config_dir/config.fish
|
||||
set -l update_args -B $__fish_data_dir/tools/create_manpage_completions.py --manpath --cleanup-in $__fish_user_data_dir/generated_completions --cleanup-in $__fish_cache_dir/generated_completions
|
||||
if set -l python (__fish_anypython)
|
||||
# Run python directly in the background and swallow all output
|
||||
$python $update_args >/dev/null 2>&1 &
|
||||
# Then disown the job so that it continues to run in case of an early exit (#6269)
|
||||
disown >/dev/null 2>&1
|
||||
# Orphan the job so that it continues to run in case of an early exit (#6269)
|
||||
/bin/sh -c '( "$@" ) >/dev/null 2>&1 &' -- $python $update_args
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -221,7 +220,11 @@ end" >$__fish_config_dir/config.fish
|
||||
|
||||
# Notify terminals when $PWD changes via OSC 7 (issue #906).
|
||||
function __fish_update_cwd_osc --on-variable PWD --description 'Notify terminals when $PWD changes'
|
||||
printf \e\]7\;file://%s%s\a $hostname (string escape --style=url -- $PWD)
|
||||
set -l host $hostname
|
||||
if set -q KONSOLE_VERSION
|
||||
set host ''
|
||||
end
|
||||
printf \e\]7\;file://%s%s\a $host (string escape --style=url -- $PWD)
|
||||
end
|
||||
__fish_update_cwd_osc # Run once because we might have already inherited a PWD from an old tab
|
||||
|
||||
|
||||
@@ -56,9 +56,13 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod
|
||||
|
||||
bind --preset $argv alt-right nextd-or-forward-word
|
||||
bind --preset $argv alt-left prevd-or-backward-word
|
||||
$legacy_bind --preset $argv \e\[1\;9C nextd-or-forward-word # iTerm2 < 3.5.12
|
||||
$legacy_bind --preset $argv \e\[1\;9D prevd-or-backward-word # iTerm2 < 3.5.12
|
||||
|
||||
bind --preset $argv alt-up history-token-search-backward
|
||||
bind --preset $argv alt-down history-token-search-forward
|
||||
$legacy_bind --preset $argv \e\[1\;9A history-token-search-backward # iTerm2 < 3.5.12
|
||||
$legacy_bind --preset $argv \e\[1\;9B history-token-search-forward # iTerm2 < 3.5.12
|
||||
# Bash compatibility
|
||||
# https://github.com/fish-shell/fish-shell/issues/89
|
||||
bind --preset $argv alt-. history-token-search-backward
|
||||
|
||||
@@ -180,8 +180,7 @@ 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 &
|
||||
disown $last_pid &>/dev/null
|
||||
/bin/sh -c '( /usr/bin/git --version; touch /tmp/__fish_git_ready ) >/dev/null 2>&1 &'
|
||||
function __fish_git_prompt_ready
|
||||
path is /tmp/__fish_git_ready || return 1
|
||||
# git is ready, erase the function.
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
function fish_vi_cursor -d 'Set cursor shape for different vi modes'
|
||||
# if stdin is not a tty, there is effectively no bind mode.
|
||||
if not test -t 0
|
||||
return
|
||||
end
|
||||
|
||||
set -q fish_cursor_unknown
|
||||
or set -g fish_cursor_unknown block
|
||||
|
||||
function __fish_vi_cursor --argument-names varname
|
||||
if not status is-interactive; and not status is-interactive-read
|
||||
return
|
||||
end
|
||||
if not set -q $varname
|
||||
switch $varname
|
||||
case fish_cursor_insert
|
||||
|
||||
@@ -239,8 +239,7 @@ function help --description 'Show help for the fish shell'
|
||||
# The space before the /c is to prevent msys2 from expanding it to a path
|
||||
$fish_browser " /c" start $page_url
|
||||
else if contains -- $fish_browser[1] $graphical_browsers
|
||||
$fish_browser $page_url &
|
||||
disown $last_pid >/dev/null 2>&1
|
||||
/bin/sh -c '( "$@" ) &' -- $fish_browser $page_url
|
||||
else
|
||||
$fish_browser $page_url
|
||||
end
|
||||
|
||||
@@ -5,18 +5,17 @@ function fish_prompt -d "Write out the prompt"
|
||||
set -l laststatus $status
|
||||
|
||||
set -l git_info
|
||||
if set -l git_branch (command git branch --format=%\(refname:lstrip=2\) 2> /dev/null)
|
||||
if git rev-parse 2>/dev/null
|
||||
set -l git_branch (
|
||||
command git symbolic-ref HEAD 2>/dev/null | string replace 'refs/heads/' ''
|
||||
or command git describe HEAD 2>/dev/null
|
||||
or echo unknown
|
||||
)
|
||||
set git_branch (set_color -o blue)"$git_branch"
|
||||
set -l git_status
|
||||
if test -n "$git_branch"; and not command git diff-index --quiet HEAD --
|
||||
if set -l count (command git rev-list --count --left-right $upstream...HEAD 2>/dev/null)
|
||||
echo $count | read -l ahead behind
|
||||
if test "$ahead" -gt 0
|
||||
set git_status "$git_status"(set_color red)⬆
|
||||
end
|
||||
if test "$behind" -gt 0
|
||||
set git_status "$git_status"(set_color red)⬇
|
||||
end
|
||||
end
|
||||
if git rev-parse --quiet --verify HEAD >/dev/null
|
||||
and not command git diff-index --quiet HEAD --
|
||||
|
||||
for i in (git status --porcelain | string sub -l 2 | sort | uniq)
|
||||
switch $i
|
||||
case "."
|
||||
@@ -35,11 +34,8 @@ function fish_prompt -d "Write out the prompt"
|
||||
end
|
||||
else
|
||||
set git_status (set_color green):
|
||||
if test -z "$git_branch"
|
||||
set git_branch -
|
||||
end
|
||||
end
|
||||
set git_info "(git$git_status"(set_color -o blue)"$git_branch"(set_color white)")"
|
||||
set git_info "(git$git_status$git_branch"(set_color white)")"
|
||||
end
|
||||
|
||||
# Disable PWD shortening by default.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
use crate::proc::{
|
||||
get_job_control_mode, get_login, is_interactive_session, set_job_control_mode, JobControl,
|
||||
};
|
||||
use crate::reader::reader_in_interactive_read;
|
||||
use crate::wutil::{waccess, wbasename, wdirname, wrealpath, Error};
|
||||
use libc::F_OK;
|
||||
use nix::errno::Errno;
|
||||
@@ -50,6 +51,7 @@ enum StatusCmd {
|
||||
STATUS_IS_FULL_JOB_CTRL,
|
||||
STATUS_IS_INTERACTIVE,
|
||||
STATUS_IS_INTERACTIVE_JOB_CTRL,
|
||||
STATUS_IS_INTERACTIVE_READ,
|
||||
STATUS_IS_LOGIN,
|
||||
STATUS_IS_NO_JOB_CTRL,
|
||||
STATUS_LINE_NUMBER,
|
||||
@@ -82,6 +84,7 @@ enum StatusCmd {
|
||||
(STATUS_IS_FULL_JOB_CTRL, "is-full-job-control"),
|
||||
(STATUS_IS_INTERACTIVE, "is-interactive"),
|
||||
(STATUS_IS_INTERACTIVE_JOB_CTRL, "is-interactive-job-control"),
|
||||
(STATUS_IS_INTERACTIVE_READ, "is-interactive-read"),
|
||||
(STATUS_IS_LOGIN, "is-login"),
|
||||
(STATUS_IS_NO_JOB_CTRL, "is-no-job-control"),
|
||||
(STATUS_SET_JOB_CONTROL, "job-control"),
|
||||
@@ -141,6 +144,7 @@ fn default() -> Self {
|
||||
const IS_FULL_JOB_CTRL_SHORT: char = '\x02';
|
||||
const IS_INTERACTIVE_JOB_CTRL_SHORT: char = '\x03';
|
||||
const IS_NO_JOB_CTRL_SHORT: char = '\x04';
|
||||
const IS_INTERACTIVE_READ_SHORT: char = '\x05';
|
||||
|
||||
const SHORT_OPTIONS: &wstr = L!(":L:cbilfnhj:t");
|
||||
const LONG_OPTIONS: &[WOption] = &[
|
||||
@@ -162,6 +166,11 @@ fn default() -> Self {
|
||||
NoArgument,
|
||||
IS_INTERACTIVE_JOB_CTRL_SHORT,
|
||||
),
|
||||
wopt(
|
||||
L!("is-interactive-read"),
|
||||
NoArgument,
|
||||
IS_INTERACTIVE_READ_SHORT,
|
||||
),
|
||||
wopt(L!("is-login"), NoArgument, 'l'),
|
||||
wopt(L!("is-no-job-control"), NoArgument, IS_NO_JOB_CTRL_SHORT),
|
||||
wopt(L!("job-control"), RequiredArgument, 'j'),
|
||||
@@ -271,6 +280,11 @@ fn parse_cmd_opts(
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
}
|
||||
IS_INTERACTIVE_READ_SHORT => {
|
||||
if !opts.try_set_status_cmd(STATUS_IS_INTERACTIVE_READ, streams) {
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
}
|
||||
IS_NO_JOB_CTRL_SHORT => {
|
||||
if !opts.try_set_status_cmd(STATUS_IS_NO_JOB_CTRL, streams) {
|
||||
return STATUS_CMD_ERROR;
|
||||
@@ -548,6 +562,13 @@ pub fn status(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> O
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
}
|
||||
STATUS_IS_INTERACTIVE_READ => {
|
||||
if reader_in_interactive_read() {
|
||||
return STATUS_CMD_OK;
|
||||
} else {
|
||||
return STATUS_CMD_ERROR;
|
||||
}
|
||||
}
|
||||
STATUS_IS_NO_JOB_CTRL => {
|
||||
if get_job_control_mode() == JobControl::none {
|
||||
return STATUS_CMD_OK;
|
||||
|
||||
@@ -369,7 +369,9 @@ fn extract_prefix_and_unescape_yaml(line: &[u8]) -> Option<(Cow<[u8]>, Cow<[u8]>
|
||||
fn decode_item_fish_2_0(mut data: &[u8]) -> Option<HistoryItem> {
|
||||
let (advance, line) = read_line(data);
|
||||
let line = trim_start(line);
|
||||
assert!(line.starts_with(b"- cmd"));
|
||||
if !line.starts_with(b"- cmd") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (_key, value) = extract_prefix_and_unescape_yaml(line)?;
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ fn readb(in_fd: RawFd, blocking: bool) -> ReadbResult {
|
||||
let select_res = fdset.check_readable(if blocking {
|
||||
FdReadableSet::kNoTimeout
|
||||
} else {
|
||||
0
|
||||
1000
|
||||
});
|
||||
if select_res < 0 {
|
||||
let err = errno::errno().0;
|
||||
@@ -437,6 +437,7 @@ pub fn update_wait_on_sequence_key_ms(vars: &EnvStack) {
|
||||
static IN_ITERM_PRE_CSI_U: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
||||
static IN_JETBRAINS: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
||||
static IN_KITTY: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
||||
static IN_WEZTERM: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
||||
|
||||
pub fn terminal_protocol_hacks() {
|
||||
use std::env::var_os;
|
||||
@@ -457,6 +458,10 @@ pub fn terminal_protocol_hacks() {
|
||||
);
|
||||
IN_KITTY
|
||||
.store(var_os("TERM").is_some_and(|term| term.as_os_str().as_bytes() == b"xterm-kitty"));
|
||||
IN_WEZTERM.store(
|
||||
var_os("TERM_PROGRAM")
|
||||
.is_some_and(|term_program| term_program.as_os_str().as_bytes() == b"WezTerm"),
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_version(version: &wstr) -> Option<(i64, i64, i64)> {
|
||||
@@ -487,12 +492,11 @@ pub fn terminal_protocols_enable_ifn() {
|
||||
|| IN_MIDNIGHT_COMMANDER_PRE_CSI_U.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() {
|
||||
} else if IN_JETBRAINS.load() || IN_ITERM_PRE_CSI_U.load() {
|
||||
// Jetbrains IDE terminals vomit CSI u
|
||||
// iTerm fails to option-modify keys
|
||||
concat!("\x1b[?2004h", "\x1b[>4;1m", "\x1b=",)
|
||||
} else if IN_KITTY.load() {
|
||||
} else if IN_KITTY.load() || IN_WEZTERM.load() {
|
||||
// Kitty spams the log for modifyotherkeys
|
||||
concat!(
|
||||
"\x1b[?2004h", // Bracketed paste
|
||||
@@ -521,11 +525,11 @@ pub(crate) fn terminal_protocols_disable_ifn() {
|
||||
}
|
||||
let sequences = if !feature_test(FeatureFlag::keyboard_protocols) {
|
||||
"\x1b[?2004l"
|
||||
} else if IN_ITERM_PRE_CSI_U.load() {
|
||||
concat!("\x1b[?2004l", "\x1b[>4;0m", "\x1b[<1u", "\x1b>",)
|
||||
} else if IN_JETBRAINS.load() || IN_ITERM_PRE_CSI_U.load() {
|
||||
concat!("\x1b[?2004l", "\x1b[>4;0m", "\x1b>",)
|
||||
} else if IN_JETBRAINS.load() {
|
||||
concat!("\x1b[?2004l", "\x1b[>4;0m", "\x1b>",)
|
||||
} else if IN_KITTY.load() {
|
||||
} else if IN_KITTY.load() || IN_WEZTERM.load() {
|
||||
// Kitty spams the log for modifyotherkeys
|
||||
concat!(
|
||||
"\x1b[?2004l", // Bracketed paste
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Generic output functions.
|
||||
use crate::color::RgbColor;
|
||||
use crate::color::{self, RgbColor};
|
||||
use crate::common::{self, wcs2string_appending};
|
||||
use crate::curses::{self, tparm1, Term};
|
||||
use crate::env::EnvVar;
|
||||
@@ -456,6 +456,35 @@ pub fn stdoutput() -> &'static RefCell<Outputter> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BufferedOuputter<'a>(&'a mut Outputter);
|
||||
|
||||
impl<'a> BufferedOuputter<'a> {
|
||||
pub fn new(outputter: &'a mut Outputter) -> Self {
|
||||
outputter.begin_buffering();
|
||||
Self(outputter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for BufferedOuputter<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.0.end_buffering();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Write for BufferedOuputter<'a> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.0
|
||||
.write(buf)
|
||||
.expect("Writing to in-memory buffer should never fail");
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.0.flush().unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a list of RgbColor, pick the "best" one, as determined by the color support. Returns
|
||||
/// RgbColor::NONE if empty.
|
||||
pub fn best_color(candidates: &[RgbColor], support: ColorSupport) -> RgbColor {
|
||||
@@ -493,6 +522,14 @@ pub fn best_color(candidates: &[RgbColor], support: ColorSupport) -> RgbColor {
|
||||
/// In particular, the argument parsing still isn't fully capable.
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
pub fn parse_color(var: &EnvVar, is_background: bool) -> RgbColor {
|
||||
let mut result = parse_color_maybe_none(var, is_background);
|
||||
if result.is_none() {
|
||||
result.typ = color::Type::Normal;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn parse_color_maybe_none(var: &EnvVar, is_background: bool) -> RgbColor {
|
||||
let mut is_bold = false;
|
||||
let mut is_underline = false;
|
||||
let mut is_italics = false;
|
||||
@@ -551,9 +588,6 @@ pub fn parse_color(var: &EnvVar, is_background: bool) -> RgbColor {
|
||||
}
|
||||
|
||||
let mut result = best_color(&candidates, get_color_support());
|
||||
if result.is_none() {
|
||||
result = RgbColor::NORMAL;
|
||||
}
|
||||
result.set_bold(is_bold);
|
||||
result.set_underline(is_underline);
|
||||
result.set_italics(is_italics);
|
||||
|
||||
@@ -183,7 +183,7 @@ fn maybe_issue_path_warning(
|
||||
)
|
||||
);
|
||||
}
|
||||
printf!("\n");
|
||||
eprintf!("\n");
|
||||
}
|
||||
|
||||
/// Finds the path of an executable named `cmd`, by looking in $PATH taken from `vars`.
|
||||
@@ -707,7 +707,7 @@ fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
// these are in varying headers. Simply hard code them.
|
||||
// Note that we treat FUSE filesystems as remote, which means we lock less on such filesystems.
|
||||
// NOTE: The cast is necessary for 32-bit systems because of the 4-byte CIFS_MAGIC_NUMBER
|
||||
match usize::try_from(buf.f_type).unwrap() {
|
||||
match buf.f_type as usize {
|
||||
0x5346414F | // AFS_SUPER_MAGIC - Andrew File System
|
||||
0x6B414653 | // AFS_FS_MAGIC - Kernel AFS and AuriStorFS
|
||||
0x73757245 | // CODA_SUPER_MAGIC - Coda File System
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
use crate::nix::isatty;
|
||||
use crate::operation_context::{get_bg_context, OperationContext};
|
||||
use crate::output::parse_color;
|
||||
use crate::output::parse_color_maybe_none;
|
||||
use crate::output::BufferedOuputter;
|
||||
use crate::output::Outputter;
|
||||
use crate::pager::{PageRendering, Pager, SelectionMotion};
|
||||
use crate::panic::AT_EXIT;
|
||||
@@ -221,6 +223,13 @@ unsafe impl Sync for ReaderDataStack {}
|
||||
unsafe { &mut *READER_DATA_STACK.0.get() }
|
||||
}
|
||||
|
||||
pub fn reader_in_interactive_read() -> bool {
|
||||
reader_data_stack()
|
||||
.iter()
|
||||
.rev()
|
||||
.any(|reader| reader.conf.exit_on_interrupt)
|
||||
}
|
||||
|
||||
/// Access the top level reader data.
|
||||
pub fn current_data() -> Option<&'static mut ReaderData> {
|
||||
reader_data_stack()
|
||||
@@ -650,8 +659,13 @@ fn read_i(parser: &Parser) -> i32 {
|
||||
data.command_line.clear();
|
||||
data.update_buff_pos(EditableLineTag::Commandline, None);
|
||||
data.command_line_changed(EditableLineTag::Commandline);
|
||||
// OSC 133 End of command
|
||||
data.screen.write_bytes(b"\x1b]133;C\x07");
|
||||
// OSC 133 "Command start"
|
||||
write!(
|
||||
BufferedOuputter::new(&mut Outputter::stdoutput().borrow_mut()),
|
||||
"\x1b]133;C;cmdline_url={}\x07",
|
||||
escape_string(&command, EscapeStringStyle::Url),
|
||||
)
|
||||
.unwrap();
|
||||
event::fire_generic(parser, L!("fish_preexec").to_owned(), vec![command.clone()]);
|
||||
let eval_res = reader_run_command(parser, &command);
|
||||
signal_clear_cancel();
|
||||
@@ -664,11 +678,12 @@ fn read_i(parser: &Parser) -> i32 {
|
||||
parser.libdata_mut().exit_current_script = false;
|
||||
|
||||
// OSC 133 "Command finished"
|
||||
let _ = write!(
|
||||
Outputter::stdoutput().borrow_mut(),
|
||||
write!(
|
||||
BufferedOuputter::new(&mut Outputter::stdoutput().borrow_mut()),
|
||||
"\x1b]133;D;{}\x07",
|
||||
parser.get_last_status()
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
event::fire_generic(parser, L!("fish_postexec").to_owned(), vec![command]);
|
||||
// Allow any pending history items to be returned in the history array.
|
||||
data.history.resolve_pending();
|
||||
@@ -1499,8 +1514,15 @@ fn paint_layout(&mut self, reason: &wstr, is_final_rendering: bool) {
|
||||
range.end = colors.len();
|
||||
}
|
||||
|
||||
let explicit_foreground = self
|
||||
.vars()
|
||||
.get_unless_empty(L!("fish_color_search_match"))
|
||||
.is_some_and(|var| !parse_color_maybe_none(&var, false).is_none());
|
||||
|
||||
for color in &mut colors[range] {
|
||||
color.foreground = HighlightRole::search_match;
|
||||
if explicit_foreground {
|
||||
color.foreground = HighlightRole::search_match;
|
||||
}
|
||||
color.background = HighlightRole::search_match;
|
||||
}
|
||||
}
|
||||
@@ -2325,7 +2347,12 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
|
||||
if c == rl::CancelCommandline {
|
||||
// Move cursor to the end of the line.
|
||||
let end = self.command_line.len();
|
||||
self.update_buff_pos(EditableLineTag::Commandline, Some(end));
|
||||
{
|
||||
let tmp =
|
||||
std::mem::replace(&mut self.cursor_end_mode, CursorEndMode::Exclusive);
|
||||
self.update_buff_pos(EditableLineTag::Commandline, Some(end));
|
||||
self.cursor_end_mode = tmp;
|
||||
}
|
||||
|
||||
self.autosuggestion.clear();
|
||||
// Repaint also changes the actual cursor position
|
||||
@@ -2570,7 +2597,7 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
|
||||
self.data
|
||||
.insert_string(self.active_edit_line_tag(), &yank_str);
|
||||
self.rls_mut().yank_len = yank_str.len();
|
||||
if self.cursor_end_mode == CursorEndMode::Inclusive {
|
||||
if !yank_str.is_empty() && self.cursor_end_mode == CursorEndMode::Inclusive {
|
||||
let (_elt, el) = self.active_edit_line();
|
||||
self.update_buff_pos(self.active_edit_line_tag(), Some(el.position() - 1));
|
||||
}
|
||||
@@ -4065,7 +4092,10 @@ fn reader_interactive_init(parser: &Parser) {
|
||||
terminal_protocol_hacks();
|
||||
IN_MIDNIGHT_COMMANDER_PRE_CSI_U.store(
|
||||
parser.vars().get_unless_empty(L!("MC_TMPDIR")).is_some()
|
||||
&& parser.vars().get_unless_empty(L!("__mc_csi_u")).is_none(),
|
||||
&& parser
|
||||
.vars()
|
||||
.get_unless_empty(L!("__mc_kitty_keyboard"))
|
||||
.is_none(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
use crate::output::Outputter;
|
||||
use crate::termsize::{termsize_last, Termsize};
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wcstringutil::string_prefixes_string;
|
||||
use crate::wcstringutil::{fish_wcwidth_visible, string_prefixes_string};
|
||||
use crate::wutil::fstat;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@@ -1636,7 +1636,7 @@ fn measure_run_from(
|
||||
width = next_tab_stop(width);
|
||||
} else {
|
||||
// Ordinary char. Add its width with care to ignore control chars which have width -1.
|
||||
width += wcwidth_rendered_min_0(input.char_at(idx));
|
||||
width += usize::try_from(fish_wcwidth_visible(input.char_at(idx))).unwrap();
|
||||
}
|
||||
idx += 1;
|
||||
}
|
||||
@@ -1682,7 +1682,7 @@ fn truncate_run(
|
||||
curr_width = measure_run_from(run, 0, None, cache);
|
||||
idx = 0;
|
||||
} else {
|
||||
let char_width = wcwidth_rendered_min_0(c);
|
||||
let char_width = usize::try_from(fish_wcwidth_visible(c)).unwrap();
|
||||
curr_width -= std::cmp::min(curr_width, char_width);
|
||||
run.remove(idx);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::wopendir;
|
||||
use crate::common::{cstr2wcstring, wcs2zstring};
|
||||
use crate::common::{str2wcstring, wcs2zstring};
|
||||
use crate::wchar::{wstr, WString};
|
||||
use crate::wutil::DevInode;
|
||||
use libc::{
|
||||
@@ -8,11 +8,10 @@
|
||||
S_IFSOCK,
|
||||
};
|
||||
use std::cell::Cell;
|
||||
use std::io::{self};
|
||||
use std::io;
|
||||
use std::os::fd::RawFd;
|
||||
use std::ptr::NonNull;
|
||||
use std::ptr::{addr_of, NonNull};
|
||||
use std::rc::Rc;
|
||||
use std::slice;
|
||||
|
||||
/// Types of files that may be in a directory.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -267,19 +266,23 @@ pub fn next(&mut self) -> Option<io::Result<&DirEntry>> {
|
||||
|
||||
// dent.d_name is c_char; pretend it's u8.
|
||||
assert!(std::mem::size_of::<libc::c_char>() == std::mem::size_of::<u8>());
|
||||
let d_name_cchar = &dent.d_name;
|
||||
let d_name = unsafe {
|
||||
slice::from_raw_parts(d_name_cchar.as_ptr() as *const u8, d_name_cchar.len())
|
||||
};
|
||||
|
||||
// Do not rely on `libc::dirent::d_name.len()` as dirent names may exceed
|
||||
// the nominal buffer size; instead use the terminating nul byte.
|
||||
// TODO: This should use &raw from Rust 1.82 on
|
||||
// https://github.com/rust-lang/libc/issues/2669
|
||||
// https://github.com/fish-shell/fish-shell/issues/11221
|
||||
let d_name_ptr = addr_of!((*dent).d_name);
|
||||
let d_name = unsafe { std::ffi::CStr::from_ptr(d_name_ptr.cast()) }.to_bytes();
|
||||
|
||||
// Skip . and ..,
|
||||
// unless we've been told not to.
|
||||
if !self.withdot && (d_name.starts_with(b".\0") || d_name.starts_with(b"..\0")) {
|
||||
if !self.withdot && (d_name == b"." || d_name == b"..") {
|
||||
return self.next();
|
||||
}
|
||||
|
||||
self.entry.reset();
|
||||
self.entry.name = cstr2wcstring(d_name);
|
||||
self.entry.name = str2wcstring(d_name);
|
||||
#[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
{
|
||||
self.entry.inode = dent.d_fileno;
|
||||
|
||||
@@ -40,12 +40,17 @@ echo "echo foo" > git-frobnicate
|
||||
chmod +x git-frobnicate
|
||||
|
||||
complete -c git-frobnicate -xa 'foo bar baz'
|
||||
complete -c git-frobnicate -l onto -xa 'onto1 onto2'
|
||||
|
||||
complete -C'git frobnicate '
|
||||
#CHECK: bar
|
||||
#CHECK: baz
|
||||
#CHECK: foo
|
||||
|
||||
complete -C'git frobnicate --onto '
|
||||
#CHECK: onto1
|
||||
#CHECK: onto2
|
||||
|
||||
complete -C'git ' | grep '^add'\t
|
||||
# (note: actual tab character in the check here)
|
||||
#CHECK: add Add file contents to the staging area
|
||||
|
||||
Reference in New Issue
Block a user