mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-11 02:51:15 -03:00
Compare commits
24 Commits
4.1.1
...
Integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fa252677a | ||
|
|
e074b27abf | ||
|
|
a5727e59da | ||
|
|
4e68f8a130 | ||
|
|
417755b6a7 | ||
|
|
ef30feda12 | ||
|
|
20e3ed23b7 | ||
|
|
7bfdb8460d | ||
|
|
8d9d5816ed | ||
|
|
97f6129dea | ||
|
|
c19de5aeb9 | ||
|
|
874fba1108 | ||
|
|
695f225cb4 | ||
|
|
9f36adb731 | ||
|
|
3ff699d7ea | ||
|
|
4b6ad751e8 | ||
|
|
8dd90085b6 | ||
|
|
47396e78ce | ||
|
|
77a038541a | ||
|
|
84dbdf4e2a | ||
|
|
0a50ba9ef5 | ||
|
|
8b317a192b | ||
|
|
42decbf1da | ||
|
|
79b0d24701 |
@@ -1,3 +1,20 @@
|
||||
fish 4.1.2 (released October 07, 2025)
|
||||
======================================
|
||||
|
||||
This release fixes the following regressions identified in 4.1.0:
|
||||
|
||||
- Fixed spurious error output when completing remote file paths for ``scp`` (:issue:`11860`).
|
||||
- Fixed the :kbd:`alt-l` binding not formatting ``ls`` output correctly (one entry per line, no colors) (:issue:`11888`).
|
||||
- Fixed an issue where focus events (currently only enabled in ``tmux``) would cause multiline prompts to be redrawn in the wrong line (:issue:`11870`).
|
||||
- Stopped printing output that would cause a glitch on old versions of Midnight Commander (:issue:`11869`).
|
||||
- Added a fix for some configurations of Zellij where :kbd:`escape` key processing was delayed (:issue:`11868`).
|
||||
- Fixed a case where the :doc:`web-based configuration tool <cmds/fish_config>` would generate invalid configuration (:issue:`11861`).
|
||||
- Fixed a case where pasting into ``fish -c read`` would fail with a noisy error (:issue:`11836`).
|
||||
- Fixed a case where upgrading fish would break old versions of fish that were still running.
|
||||
|
||||
In general, fish still needs to be restarted after it is upgraded,
|
||||
except for `standalone builds <https://github.com/fish-shell/fish-shell/?tab=readme-ov-file#building-fish-with-embedded-data-experimental>`__.
|
||||
|
||||
fish 4.1.1 (released September 30, 2025)
|
||||
========================================
|
||||
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -105,7 +105,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "4.1.1"
|
||||
version = "4.1.2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
|
||||
@@ -64,7 +64,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.1.1"
|
||||
version = "4.1.2"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
default-run = "fish"
|
||||
|
||||
2
build.rs
2
build.rs
@@ -16,7 +16,7 @@ fn main() {
|
||||
// language server.
|
||||
|
||||
rsconf::set_env_value(
|
||||
"FISH_BUILD_DIR",
|
||||
"FISH_RESOLVED_BUILD_DIR",
|
||||
// If set by CMake, this might include symlinks. Since we want to compare this to the
|
||||
// dir fish is executed in we need to canonicalize it.
|
||||
canonicalize(fish_build_dir()).to_str().unwrap(),
|
||||
|
||||
@@ -32,6 +32,7 @@ done
|
||||
|
||||
repo_root="$(dirname "$0")/.."
|
||||
fish_site=$repo_root/../fish-site
|
||||
fish_site_repo=git@github.com:$repository_owner/fish-site
|
||||
|
||||
for path in . "$fish_site"
|
||||
do
|
||||
@@ -42,6 +43,13 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cd "$fish_site"
|
||||
[ "$(git rev-parse HEAD)" = \
|
||||
"$(git ls-remote "$fish_site_repo" refs/heads/master |
|
||||
awk '{print $1}')" ]
|
||||
)
|
||||
|
||||
if git tag | grep -qxF "$version"; then
|
||||
echo >&2 "$0: tag $version already exists"
|
||||
exit 1
|
||||
@@ -147,15 +155,21 @@ rm -rf "$tmpdir"
|
||||
" | sed 's,^\s*| \?,,')"
|
||||
)
|
||||
|
||||
# Approve macos-codesign
|
||||
# TODO what if current user can't approve?
|
||||
gh_pending_deployments() {
|
||||
gh_api_repo() {
|
||||
path=$1
|
||||
shift
|
||||
command gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$repository_owner/fish-shell/actions/runs/$run_id/pending_deployments" \
|
||||
"/repos/$repository_owner/fish-shell/$path" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
# Approve macos-codesign
|
||||
# TODO what if current user can't approve?
|
||||
gh_pending_deployments() {
|
||||
gh_api_repo "actions/runs/$run_id/pending_deployments" "$@"
|
||||
}
|
||||
while {
|
||||
environment_id=$(gh_pending_deployments | jq .[].environment.id)
|
||||
[ -z "$environment_id" ]
|
||||
@@ -170,7 +184,7 @@ echo '
|
||||
"comment": "Approved via ./build_tools/release.sh"
|
||||
}
|
||||
' |
|
||||
gh_pending_deployments -XPOST --input=-
|
||||
gh_pending_deployments --method POST --input=-
|
||||
|
||||
# Await completion.
|
||||
gh run watch "$run_id"
|
||||
@@ -198,12 +212,12 @@ done
|
||||
" | sed 's,^\s*| \?,,')"
|
||||
# This takes care to support remote names that are different from
|
||||
# fish-shell remote name. Also, support detached HEAD state.
|
||||
git push git@github.com:$repository_owner/fish-site HEAD:master
|
||||
git push "$fish_site_repo" HEAD:master
|
||||
)
|
||||
|
||||
if [ -n "$integration_branch" ]; then
|
||||
if [ -n "$integration_branch" ]; then {
|
||||
git push $remote "$version^{commit}":refs/heads/$integration_branch
|
||||
else
|
||||
} else {
|
||||
changelog=$(cat - CHANGELOG.rst <<EOF
|
||||
fish ?.?.? (released ???)
|
||||
=========================
|
||||
@@ -213,23 +227,27 @@ EOF
|
||||
printf %s\\n "$changelog" >CHANGELOG.rst
|
||||
CommitVersion ${version}-snapshot "start new cycle"
|
||||
git push $remote HEAD:master
|
||||
fi
|
||||
|
||||
# TODO This can currently require a TTY for editing and password
|
||||
# prompts.
|
||||
if [ "$repository_owner" = fish-shell ]; then {
|
||||
mail=$(mktemp)
|
||||
cat >$mail <<EOF
|
||||
From: $(git var GIT_AUTHOR_IDENT | sed 's/ [0-9]* +[0-9]*$//')
|
||||
Subject: fish $version released
|
||||
|
||||
See https://github.com/fish-shell/fish-shell/releases/tag/$version
|
||||
EOF
|
||||
git send-email --suppress-cc=all --confirm=always $mail \
|
||||
--to="fish-users Mailing List <fish-users@lists.sourceforge.net>"
|
||||
rm $mail
|
||||
} fi
|
||||
|
||||
milestone_number=$(
|
||||
gh_api_repo milestones?state=open |
|
||||
jq '.[] | select(.title == "fish '"$version"'") | .number'
|
||||
)
|
||||
gh_api_repo --method PATCH milestones/$milestone_number \
|
||||
--raw-field state=closed
|
||||
|
||||
next_patch_version=$(
|
||||
echo "$version" | awk -F. '
|
||||
NF == 3 && $3 ~ /[0-9]+/ {
|
||||
printf "%s.%s.%s", $1, $2, $3+1
|
||||
}
|
||||
'
|
||||
)
|
||||
if [ -n "$next_patch_version" ]; then
|
||||
gh_api_repo --method POST milestones \
|
||||
--raw-field title="fish $next_patch_version"
|
||||
fi
|
||||
|
||||
exit
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ fn cargo_target_dir() -> Cow<'static, Path> {
|
||||
}
|
||||
|
||||
pub fn fish_build_dir() -> Cow<'static, Path> {
|
||||
// FISH_BUILD_DIR is set by CMake, if we are using it.
|
||||
// This is set if using CMake.
|
||||
option_env!("FISH_BUILD_DIR")
|
||||
.map(|d| Cow::Borrowed(Path::new(d)))
|
||||
.unwrap_or(cargo_target_dir())
|
||||
|
||||
@@ -47,7 +47,7 @@ complete -c scp -d "Local Path" -n "not string match @ -- (commandline -ct)"
|
||||
complete -c scp -d "Remote Path" -f -n "commandline -ct | string match -e ':'" -a '
|
||||
(__scp_remote_target):(
|
||||
if not set -q __fish_scp_sftp
|
||||
set -l tmp (__fish_mktemp fish-scp)
|
||||
set -l tmp (__fish_mktemp_relative fish-scp)
|
||||
if scp -P(__scp2ssh_port_number) -o "BatchMode yes" -q -O $tmp (__scp_remote_target):/dev/null
|
||||
set -g __fish_scp_sftp true
|
||||
else
|
||||
|
||||
@@ -10,7 +10,8 @@ set -l erase_line "$(
|
||||
function __fish_echo --inherit-variable erase_line --description 'run the given command after the current commandline and redraw the prompt'
|
||||
set -l line (commandline --line)
|
||||
string >&2 repeat -N \n --count=(math (commandline | count) - $line + 1)
|
||||
printf %s\n $erase_line($argv) >&2
|
||||
printf %s $erase_line >&2
|
||||
$argv >&2
|
||||
string >&2 repeat -N \n --count=(math (count (fish_prompt)) - 1)
|
||||
string >&2 repeat -N \n --count=(math $line - 1)
|
||||
commandline -f repaint
|
||||
|
||||
@@ -91,7 +91,7 @@ function fish_config --description "Launch fish's web based configuration"
|
||||
switch $cmd
|
||||
case show
|
||||
set -l fish (status fish-path)
|
||||
set -l prompts (__fish_config_matching tools/web_config/sample_prompts .fish $argv)
|
||||
set -l prompts (dirs=$prompt_dir __fish_config_matching tools/web_config/sample_prompts .fish $argv)
|
||||
for p in $prompts
|
||||
set -l promptname (string replace -r '.*/([^/]*).fish$' '$1' $p)
|
||||
echo -s (set_color --underline) $promptname (set_color normal)
|
||||
@@ -240,8 +240,7 @@ function fish_config --description "Launch fish's web based configuration"
|
||||
case show
|
||||
set -l fish (status fish-path)
|
||||
set -l themes \
|
||||
(path filter $dirs/$argv.theme) \
|
||||
(__fish_config_matching tools/web_config/themes .theme $argv)
|
||||
(dirs=$dirs __fish_config_matching tools/web_config/themes .theme $argv)
|
||||
set -l used_themes
|
||||
|
||||
echo -s (set_color normal; set_color --underline) Current (set_color normal)
|
||||
@@ -384,17 +383,16 @@ function __fish_config_matching
|
||||
set -l suffix $argv[2]
|
||||
set -e argv[1..2]
|
||||
set -l paths
|
||||
if set -q __fish_data_dir[1]
|
||||
if not set -q argv[1]
|
||||
set paths $__fish_data_dir/$prefix/*$suffix
|
||||
else
|
||||
set paths (path filter $__fish_data_dir/$prefix/$argv$suffix)
|
||||
end
|
||||
if not set -q argv[1]
|
||||
set paths $dirs/*$suffix
|
||||
else
|
||||
set paths (path filter $dirs/$argv$suffix)
|
||||
end
|
||||
if not set -q __fish_data_dir[1]
|
||||
if not set -q argv[1]
|
||||
set paths (status list-files $prefix)
|
||||
set -a paths (status list-files $prefix)
|
||||
else
|
||||
set paths (status list-files $prefix | grep -Fx -e"$prefix/"$argv$suffix)
|
||||
set -a paths (status list-files $prefix | grep -Fx -e"$prefix/"$argv$suffix)
|
||||
end
|
||||
end
|
||||
string join \n $paths
|
||||
|
||||
@@ -229,7 +229,7 @@ def parse_color(color_str):
|
||||
background_color = ""
|
||||
underline_color = ""
|
||||
bold = False
|
||||
underline = False
|
||||
underline = None
|
||||
italics = False
|
||||
dim = False
|
||||
reverse = False
|
||||
@@ -263,6 +263,10 @@ def parse_color(color_str):
|
||||
) -> str:
|
||||
if comp.startswith(long_opt):
|
||||
c = comp[len(long_opt) :]
|
||||
if c[0] == "=":
|
||||
# There was a = between the long option and the value.
|
||||
# i.e. support also --background=red, not just --background red
|
||||
c = c[1:]
|
||||
parsed_c = parse_one_color(c)
|
||||
# We prefer the unparsed version - if it says "brgreen", we use brgreen,
|
||||
# instead of 00ff00
|
||||
|
||||
@@ -635,7 +635,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
||||
transient = parser.libdata().transient_commandline.clone().unwrap();
|
||||
current_buffer = &transient;
|
||||
current_cursor_pos = transient.len();
|
||||
} else if is_interactive_session() {
|
||||
} else if parser.interactive_initialized.load() || is_interactive_session() {
|
||||
current_buffer = &rstate.text;
|
||||
current_cursor_pos = rstate.cursor_pos;
|
||||
} else {
|
||||
|
||||
@@ -458,7 +458,7 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
||||
None => {
|
||||
// No argument given, try to use the current commandline.
|
||||
let commandline_state = commandline_get_state(true);
|
||||
if !is_interactive_session() {
|
||||
if !parser.interactive_initialized.load() && !is_interactive_session() {
|
||||
streams.err.append(cmd);
|
||||
streams
|
||||
.err
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
use std::sync::{Arc, MutexGuard};
|
||||
use std::time;
|
||||
|
||||
pub const BUILD_DIR: &str = env!("FISH_RESOLVED_BUILD_DIR");
|
||||
|
||||
pub const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
|
||||
// Highest legal ASCII value.
|
||||
@@ -1654,7 +1656,7 @@ pub fn get_executable_path(argv0: impl AsRef<Path>) -> PathBuf {
|
||||
// When /proc/self/exe points to a file that was deleted (or overwritten on update!)
|
||||
// then linux adds a " (deleted)" suffix.
|
||||
// If that's not a valid path, let's remove that awkward suffix.
|
||||
if !path.ends_with(" (deleted)") {
|
||||
if path.as_os_str().as_bytes().ends_with(b" (deleted)") {
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
13
src/env/config_paths.rs
vendored
13
src/env/config_paths.rs
vendored
@@ -1,7 +1,7 @@
|
||||
use crate::common::wcs2string;
|
||||
use crate::common::BUILD_DIR;
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::{common::get_executable_path, FLOG, FLOGF};
|
||||
#[cfg(not(feature = "embed-data"))]
|
||||
use fish_build_helper::workspace_root;
|
||||
use std::ffi::OsString;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
@@ -63,7 +63,14 @@ fn static_paths() -> Self {
|
||||
fn from_exec_path(exec_path: PathBuf) -> Self {
|
||||
FLOG!(config, "embed-data feature is active, ignoring data paths");
|
||||
Self {
|
||||
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
|
||||
sysconf: if exec_path
|
||||
.canonicalize()
|
||||
.is_ok_and(|exec_path| exec_path.starts_with(BUILD_DIR))
|
||||
{
|
||||
workspace_root().join("etc")
|
||||
} else {
|
||||
PathBuf::from(SYSCONF_DIR).join("fish")
|
||||
},
|
||||
bin: exec_path.parent().map(|x| x.to_path_buf()),
|
||||
}
|
||||
}
|
||||
@@ -116,7 +123,7 @@ fn from_exec_path(unresolved_exec_path: PathBuf) -> Self {
|
||||
}
|
||||
|
||||
// If we're in Cargo's target directory or in CMake's build directory, use the source files.
|
||||
if exec_path.starts_with(env!("FISH_BUILD_DIR")) {
|
||||
if exec_path.starts_with(BUILD_DIR) {
|
||||
FLOG!(
|
||||
config,
|
||||
format!(
|
||||
|
||||
@@ -452,6 +452,8 @@ fn update_fish_color_support(vars: &EnvStack) {
|
||||
crate::terminal::set_color_support(color_support);
|
||||
}
|
||||
|
||||
pub const MIDNIGHT_COMMANDER_SID: &wstr = L!("MC_SID");
|
||||
|
||||
// Initialize the terminal subsystem
|
||||
fn init_terminal(vars: &EnvStack) {
|
||||
let term = vars.get(L!("TERM"));
|
||||
@@ -464,7 +466,7 @@ fn init_terminal(vars: &EnvStack) {
|
||||
IS_DUMB.store(term == "dumb");
|
||||
ONLY_GRAYSCALE.store(term == "ansi-m" || term == "linux-m" || term == "xterm-mono");
|
||||
|
||||
if vars.get(L!("MC_SID")).is_some() {
|
||||
if vars.get(MIDNIGHT_COMMANDER_SID).is_some() {
|
||||
screen_set_midnight_commander_hack();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
use crate::reader::reader_test_and_clear_interrupted;
|
||||
use crate::threads::iothread_port;
|
||||
use crate::tty_handoff::{
|
||||
get_kitty_keyboard_capability, maybe_set_kitty_keyboard_capability,
|
||||
maybe_set_scroll_content_up_capability, SCROLL_CONTENT_UP_TERMINFO_CODE, XTVERSION,
|
||||
maybe_set_kitty_keyboard_capability, maybe_set_scroll_content_up_capability,
|
||||
SCROLL_CONTENT_UP_TERMINFO_CODE, XTVERSION,
|
||||
};
|
||||
use crate::universal_notifier::default_notifier;
|
||||
use crate::wchar::{encode_byte_to_char, prelude::*};
|
||||
@@ -983,16 +983,13 @@ fn try_readb(&mut self, buffer: &mut Vec<u8>) -> Option<u8> {
|
||||
let fd = self.get_in_fd();
|
||||
if !check_fd_readable(
|
||||
fd,
|
||||
Duration::from_millis(
|
||||
if self.paste_is_buffering()
|
||||
|| self.is_blocked_querying()
|
||||
|| get_kitty_keyboard_capability() == Some(&true)
|
||||
{
|
||||
300
|
||||
} else {
|
||||
1
|
||||
},
|
||||
),
|
||||
Duration::from_millis(if self.paste_is_buffering() || self.is_blocked_querying() {
|
||||
300
|
||||
} else if buffer == b"\x1b" {
|
||||
1 // distinguish legacy escape
|
||||
} else {
|
||||
30
|
||||
}),
|
||||
) {
|
||||
FLOG!(
|
||||
reader,
|
||||
|
||||
@@ -585,10 +585,10 @@ fn make_base_directory(xdg_var: &wstr, non_xdg_homepath: &wstr) -> BaseDirectory
|
||||
// the actual $HOME or $XDG_XXX directories. This prevents the tests from failing and/or stops
|
||||
// the tests polluting the user's actual $HOME if a sandbox environment has not been set up.
|
||||
{
|
||||
use crate::common::str2wcstring;
|
||||
use crate::common::{str2wcstring, BUILD_DIR};
|
||||
use std::path::PathBuf;
|
||||
|
||||
let mut build_dir = PathBuf::from(env!("FISH_BUILD_DIR"));
|
||||
let mut build_dir = PathBuf::from(BUILD_DIR);
|
||||
build_dir.push("fish-test-home");
|
||||
|
||||
let err = match std::fs::create_dir_all(&build_dir) {
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
use crate::env::EnvStack;
|
||||
use crate::env::{EnvMode, Environment, Statuses};
|
||||
use crate::env_dispatch::guess_emoji_width;
|
||||
use crate::env_dispatch::MIDNIGHT_COMMANDER_SID;
|
||||
use crate::exec::exec_subshell;
|
||||
use crate::expand::expand_one;
|
||||
use crate::expand::{expand_string, expand_tilde, ExpandFlags, ExpandResultCode};
|
||||
@@ -260,9 +261,9 @@ fn redirect_tty_after_sighup() {
|
||||
}
|
||||
}
|
||||
|
||||
fn querying_allowed() -> bool {
|
||||
fn querying_allowed(vars: &dyn Environment) -> bool {
|
||||
future_feature_flags::test(FeatureFlag::query_term) &&
|
||||
!is_dumb() && std::env::var_os("MC_TMPDIR").is_none()
|
||||
!is_dumb() && vars.get(MIDNIGHT_COMMANDER_SID).is_none()
|
||||
// Could use /dev/tty in future.
|
||||
&& isatty(STDOUT_FILENO)
|
||||
}
|
||||
@@ -278,10 +279,10 @@ pub fn terminal_init(vars: &dyn Environment, inputfd: RawFd) -> InputEventQueue
|
||||
);
|
||||
|
||||
let _init_tty_metadata = ScopeGuard::new((), |()| {
|
||||
initialize_tty_protocols();
|
||||
initialize_tty_protocols(vars);
|
||||
});
|
||||
|
||||
if !querying_allowed() {
|
||||
if !querying_allowed(vars) {
|
||||
return input_queue;
|
||||
}
|
||||
|
||||
@@ -1586,7 +1587,7 @@ pub fn mouse_left_click(&mut self, cursor: ViewportPosition, click_position: Vie
|
||||
self.pager.selected_completion_idx = Some(idx);
|
||||
self.pager_selection_changed();
|
||||
}
|
||||
_ => {}
|
||||
CharOffset::Pager(_) | CharOffset::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1644,7 +1645,7 @@ pub fn combine_command_and_autosuggestion(
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
pub fn request_cursor_position(&mut self, out: &mut Outputter, q: CursorPositionQuery) {
|
||||
if !querying_allowed() {
|
||||
if !querying_allowed(self.vars()) {
|
||||
return;
|
||||
}
|
||||
let mut query = self.blocking_query();
|
||||
@@ -2629,9 +2630,11 @@ fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlo
|
||||
ImplicitEvent::QueryInterrupted => (),
|
||||
ImplicitEvent::FocusIn => {
|
||||
event::fire_generic(self.parser, L!("fish_focus_in").to_owned(), vec![]);
|
||||
self.save_screen_state();
|
||||
}
|
||||
ImplicitEvent::FocusOut => {
|
||||
event::fire_generic(self.parser, L!("fish_focus_out").to_owned(), vec![]);
|
||||
self.save_screen_state();
|
||||
}
|
||||
ImplicitEvent::DisableMouseTracking => {
|
||||
Outputter::stdoutput()
|
||||
|
||||
@@ -629,8 +629,26 @@ pub fn offset_in_cmdline_given_cursor(
|
||||
0
|
||||
});
|
||||
let y = y.min(self.actual.line_count() - 1);
|
||||
let viewport_prompt_x = viewport_cursor.x - self.actual.cursor.x;
|
||||
let x = viewport_position.x - viewport_prompt_x;
|
||||
let Some(viewport_prompt_x) = viewport_cursor.x.checked_sub(self.actual.cursor.x) else {
|
||||
FLOGF!(
|
||||
reader,
|
||||
"Actual cursor x=%d exceeds reported cursor x=%d, \
|
||||
was the cursor moved by printing to the TTY?",
|
||||
self.actual.cursor.x,
|
||||
viewport_cursor.x
|
||||
);
|
||||
return CharOffset::None;
|
||||
};
|
||||
let Some(x) = viewport_position.x.checked_sub(viewport_prompt_x) else {
|
||||
FLOGF!(
|
||||
reader,
|
||||
"Computed prompt x=%d exceeds mouse click x=%d, \
|
||||
was the cursor moved by printing to the TTY?",
|
||||
viewport_prompt_x,
|
||||
viewport_position.x
|
||||
);
|
||||
return CharOffset::None;
|
||||
};
|
||||
let line = self.actual.line(y);
|
||||
let x = x.max(line.indentation);
|
||||
let offset = line
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
mod wgetopt;
|
||||
|
||||
pub mod prelude {
|
||||
use crate::common::{ScopeGuard, ScopeGuarding};
|
||||
use crate::common::{ScopeGuard, ScopeGuarding, BUILD_DIR};
|
||||
use crate::env::{env_init, misc_init};
|
||||
use crate::parser::{CancelBehavior, Parser};
|
||||
use crate::reader::{reader_deinit, reader_init};
|
||||
@@ -88,9 +88,9 @@ fn deref(&self) -> &Self::Target {
|
||||
pub fn test_init() -> impl ScopeGuarding<Target = ()> {
|
||||
static DONE: OnceCell<()> = OnceCell::new();
|
||||
DONE.get_or_init(|| {
|
||||
// If we are building with `cargo build` and have build w/ `cmake`, FISH_BUILD_DIR might
|
||||
// not yet exist.
|
||||
let mut test_dir = PathBuf::from(env!("FISH_BUILD_DIR"));
|
||||
// If we are building with `cargo build` and have build w/ `cmake`, this might not
|
||||
// yet exist.
|
||||
let mut test_dir = PathBuf::from(BUILD_DIR);
|
||||
test_dir.push("fish-test");
|
||||
std::fs::create_dir_all(&test_dir).unwrap();
|
||||
set_current_dir(&test_dir).unwrap();
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
//! and reclaiming it after.
|
||||
|
||||
use crate::common::{self, safe_write_loop};
|
||||
use crate::env::Environment;
|
||||
use crate::env_dispatch::MIDNIGHT_COMMANDER_SID;
|
||||
use crate::flog::{FLOG, FLOGF};
|
||||
use crate::global_safety::RelaxedAtomicBool;
|
||||
use crate::job_group::JobGroup;
|
||||
@@ -25,11 +27,6 @@
|
||||
/// Whether kitty keyboard protocol support is present in the TTY.
|
||||
static KITTY_KEYBOARD_SUPPORTED: OnceCell<bool> = OnceCell::new();
|
||||
|
||||
/// Get whether the TTY supports the kitty keyboard protocol.
|
||||
pub fn get_kitty_keyboard_capability() -> Option<&'static bool> {
|
||||
KITTY_KEYBOARD_SUPPORTED.get()
|
||||
}
|
||||
|
||||
/// Set that the TTY supports the kitty keyboard protocol.
|
||||
pub fn maybe_set_kitty_keyboard_capability() {
|
||||
KITTY_KEYBOARD_SUPPORTED.get_or_init(|| true);
|
||||
@@ -60,6 +57,8 @@ pub fn xtversion() -> Option<&'static wstr> {
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum TtyQuirks {
|
||||
None,
|
||||
// Running Midnight Commander which can't parse CSI yet.
|
||||
PreCsiMidnightCommander,
|
||||
// Running in iTerm2 before 3.5.12, which causes issues when using the kitty keyboard protocol.
|
||||
PreKittyIterm2,
|
||||
// Whether we are running under tmux.
|
||||
@@ -70,9 +69,13 @@ pub enum TtyQuirks {
|
||||
|
||||
impl TtyQuirks {
|
||||
// Create a new TtyQuirks instance with the current environment.
|
||||
fn detect(xtversion: &wstr) -> Self {
|
||||
fn detect(vars: &dyn Environment, xtversion: &wstr) -> Self {
|
||||
use TtyQuirks::*;
|
||||
if get_iterm2_version(xtversion).is_some_and(|v| v < (3, 5, 12)) {
|
||||
if vars.get(MIDNIGHT_COMMANDER_SID).is_some()
|
||||
&& vars.get(L!("__mc_kitty_keyboard")).is_none()
|
||||
{
|
||||
PreCsiMidnightCommander
|
||||
} else if get_iterm2_version(xtversion).is_some_and(|v| v < (3, 5, 12)) {
|
||||
PreKittyIterm2
|
||||
} else if xtversion.starts_with(L!("tmux ")) {
|
||||
Tmux
|
||||
@@ -148,7 +151,10 @@ impl TtyQuirks {
|
||||
// Determine which keyboard protocol.
|
||||
// This is used from a signal handler.
|
||||
fn safe_get_supported_protocol(&self) -> ProtocolKind {
|
||||
use TtyQuirks::{PreKittyIterm2, Wezterm};
|
||||
use TtyQuirks::{PreCsiMidnightCommander, PreKittyIterm2, Wezterm};
|
||||
if *self == PreCsiMidnightCommander {
|
||||
return ProtocolKind::None;
|
||||
}
|
||||
if *self == PreKittyIterm2 {
|
||||
return ProtocolKind::Other;
|
||||
}
|
||||
@@ -232,7 +238,7 @@ fn tty_protocols() -> Option<&'static TtyProtocolsSet> {
|
||||
}
|
||||
|
||||
// Initialize serialized commands for enabling/disabling TTY protocols in signal handlers.
|
||||
pub fn initialize_tty_protocols() {
|
||||
pub fn initialize_tty_protocols(vars: &dyn Environment) {
|
||||
// Default missing query responses.
|
||||
KITTY_KEYBOARD_SUPPORTED.get_or_init(|| false);
|
||||
SCROLL_CONTENT_UP_SUPPORTED.get_or_init(|| false);
|
||||
@@ -243,7 +249,7 @@ pub fn initialize_tty_protocols() {
|
||||
let mut p = TTY_PROTOCOLS.load(Acquire);
|
||||
if p.is_null() {
|
||||
// Try to swap in a new TTY protocols set.
|
||||
p = Box::into_raw(Box::new(TtyQuirks::detect(xtversion).get_protocols()));
|
||||
p = Box::into_raw(Box::new(TtyQuirks::detect(vars, xtversion).get_protocols()));
|
||||
if let Err(_e) = TTY_PROTOCOLS.compare_exchange(std::ptr::null_mut(), p, Release, Acquire) {
|
||||
// Safety: p comes from Box::into_raw right above,
|
||||
// and wasn't shared with any other thread.
|
||||
|
||||
12
tests/checks/config-paths-standalone.fish
Normal file
12
tests/checks/config-paths-standalone.fish
Normal file
@@ -0,0 +1,12 @@
|
||||
# RUN: %fish -d config | grep -v ^Debug.enabled
|
||||
# REQUIRES: %fish -c 'status build-info' | grep '^Features:.*embed-data'
|
||||
|
||||
# CHECKERR: config: executable path: {{.*}}/fish
|
||||
# CHECKERR: config: embed-data feature is active, ignoring data paths
|
||||
|
||||
# NOTE: When our executable is located outside the workspace, this is "/etc".
|
||||
# CHECKERR: config: paths.sysconf: {{.+}}/etc
|
||||
|
||||
# CHECKERR: config: paths.bin: {{.*}}
|
||||
# CHECKERR: config: sourcing {{.+}}/etc/config.fish
|
||||
# CHECKERR: config: not sourcing {{.*}}/xdg_config_home/fish/config.fish (not readable or does not exist)
|
||||
@@ -23,6 +23,21 @@ fish_config theme show "fish default"
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[m}}echo{{\x1b\[m}} {{\x1b\[91m}}'{{\x1b\[33m}}Errors are the portal to discovery
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[m}}Th{{\x1b\[m}}{{\x1b\[90m}}is an autosuggestion
|
||||
|
||||
fish_config theme show | grep -E 'fish default|Default Dark'
|
||||
mkdir $__fish_config_dir/themes
|
||||
touch $__fish_config_dir/themes/custom-from-userconf.theme
|
||||
fish_config theme show | grep -E 'fish default|Default Dark|custom-from-userconf'
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}custom-from-userconf{{\x1b\[m}}
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}Base16 Default Dark{{\x1b\[m}}
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}fish default{{\x1b\[m}}
|
||||
|
||||
# Override a default theme with different colors.
|
||||
{
|
||||
status get-file tools/web_config/themes/None.theme 2>/dev/null ||
|
||||
cat $__fish_data_dir/tools/web_config/themes/None.theme
|
||||
} >$__fish_config_dir/themes/"fish default.theme"
|
||||
fish_config theme show | grep -E 'fish default|Base16 Default Dark' -A1
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}fish default{{\x1b\[m}}
|
||||
# CHECK: {{\x1b\[m}}/bright/vixens{{\x1b\[m}} {{\x1b\[m}}jump{{\x1b\[m}}{{.*}}
|
||||
# CHECK: --
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}Base16 Default Dark{{\x1b\[m}}
|
||||
# CHECK: {{.*}}/bright/vixens{{.*}}
|
||||
|
||||
@@ -61,7 +61,6 @@ def makeenv(script_path: Path, home: Path) -> Dict[str, str]:
|
||||
"XDG_DATA_DIRS",
|
||||
"LANGUAGE",
|
||||
"MC_SID",
|
||||
"MC_TMPDIR",
|
||||
"COLORTERM",
|
||||
"KONSOLE_VERSION",
|
||||
"STY",
|
||||
|
||||
Reference in New Issue
Block a user