It does not make sense to change these.
If anything, we should make them computed electric variable, so they
change whenever one of $HOME, $XDG_CONFIG_HOME and friends change.
Make them electric variables, purely because that's our only
way of marking variables as read-only. In future we can add a
EnvVarFlags::READ_ONLY instead. We'll want this anyway for "set
--read-only".
As of 7640e95bd7 (Create user config file/directories only on
first startup again, 2025-12-29) we interpret absence of the
__fish_initialized variable as "this is the first run, so create
~/.config/fish/conf.d etc.".
As reported in #11226, the presence of this universal variable causes
friction to users who track ~/.config/fish/fish_variables in version
control.
Also, __fish_initialized is the only universal variable we create
by default.
Use another way to detect the first run: since we already create
~/.config/fish on every run, assume that a successful mkdir() means
we should also create the subdirectories.
This has false negatives (if the user already created the directory)
and false positives (if the user doesn't want ~/.config/fish to exist)
but at least the latter should not really matter because historically
we always created it, at least for ~/.config/fish/fish_variables.
Alternatively, we could create a file at ~/.cache/fish/first-run-done.
But let's not add more state unless there's a good reason.
This was added in d1fd3d5825 (Detect at startup whether config and
data paths are remote, 2021-05-08); I don't understand why
this would be necessary. On my system, statfs("/tmp") and
statfs("/tmp/") both yields f_type=TMPFS_MAGIC.
In addition to the entry in ELECTRIC_VARIABLES, each computed electric
variable has a piece of code in "try_get_computed()". Makes more
sense to store this directly in the electric variable itself, no?
use consts for the getter functions, so we can reuse the type.
This is a bit weird..
try_get_computed() returns None if the variable is not computed.
That's the case for "$history" on (future) background threads.
This is a weird edge case, because we don't ever return None for any
other electric variable. This will become especially apparent when
an upcoming commit adds the computing-function to ElectricVar.
Given that the main thread already can't use "$history" from being
used as general purpose variable, there seems to be little harm in
banning it for all of them.
parse_escape_sequence() can self-recurse once, to parse sequences of
the form "\e\e...", where we attempt to interpret the first byte as
either legacy alt (if the "\e..." suffix decodes to a key), or legacy
escape (if it doesn't).
On "\e\e]11;rgb:ffff/ffff/ffff\e\\", i.e. with the inner sequence
being an OSC which doesn't produce a key event, we (tragically)
stop parsing the sequence and assume legacy escape.
Rewrite the logic to keep parsing whole escape sequences whenever
possible. Also, don't drop any leading \e even if we later error.
Also, make sure each key event gets the correct "originating sequence".
This makes
tmux new-session fish -C '
bind alt-b "echo alt-b"
bind escape "echo escape"
bind alt-escape "echo alt-escape"
sleep 2
'
output the following after quickly pressing "escape" five times
followed by "b" one time:
escape
alt-escape
escape
alt-b
which is weird but fine because "alt-escape" is rarely used.
If this proves to be a problem, we could disable translation of "\e\e"
to "alt-escape"?
Closes#12379
Electric variables are largely an implementation detail, and a following
commit wants to add a circular dependency between the electric var
impl and environment_impl. Specifically, "ElectricVar" will know
about "EnvScopedImpl". Move both impls into a common impl module,
to make this dependency less surprising.
A following commit wants to make "on_byte_read()" potentially return
multiple CharEvent values, in case there is a legacy escape.
Today "parse_csi()" and friends simply insert into the input queue.
This makes it hard to get the ordering right when we return multiple
events.
Change the parsing functions to return their result rather than
queuing directly.
This directory is no longer since 0fafff2c89 (CMake: stop installing
embedded files, 2026-05-08). Instead of telling users to see "status
list-files", refer them to the source directly.
Gets rid of another use of XDG_CACHE_HOME; this gets us closer to
a single source of truth. Not super important right now, but will
be helpful if we allow users to override XDG_CACHE_HOME in future.
I guess we could also remove it from create_manpage_completions.py,
that's unnecessary breakage but probably fine since this is not
public-facing.
A following commit wants to inject mock input bytes in unit tests.
Allow this in a quick-and-dirty way by making the input-byte-yielding an
overridable trait method again. Unfortunately this needs to be in the
shared trait; we can't specialize a private trait since specialization
is not supported in stable Rust.
Commit af137e5e96 (scrollback-push to query for indn/cuu via XTGETTCAP,
2025-01-05) extracted a bool variable
recursive_invocation = buffer.len() == 2
It replaced another instance of "buffer.len() == 2" with that variable,
but that's wrong because buffer has grown since then (so it's either
2 or 3). Flip the bool to fix this.
While at it, tighten an assertion.
Since commit ea998b03f2 (First stab at directory transition. Test with
care..., 2006-10-19), "path_get_config()"
returns an empty string (now Option) if "~/.config/fish" is inaccessible.
This is okay in C++/Rust source code, because
it makes it very obvious to the user of "path_get_config()"
that they probably don't want to try to access these files.
However commit 3855c2217f (Remove scripted XDG_CONFIG_HOME uses,
2018-12-14) made us compute $__fish_config_dir like this:
wcstring user_config_dir;
path_get_config(user_config_dir);
env_set_one(FISH_CONFIG_DIR, ENV_GLOBAL, user_config_dir);
with the (inadvertent?) change that we set $__fish_config_dir to the
empty string if the directory is inaccessible.
This situation causes us to stat weird paths like "/functions"
"/completions/", "/conf.d". This is usually inconsequential because
these paths usually don't exist, but still, we shouldn't do this.
Fix this by setting the variables to the actual value we tried,
reverting the inadvertent behavior change of 3855c2217f.
Commit 2971887bbd (Access fish_history from $XDG_DATA_HOME, 2015-09-16)
changed the history file path to ~/.local/share/fish/fish_history.
Migration is done by commit b13f0701a4 (Migrate fish_history from
config to data dir, 2015-09-18).
This should no longer be needed in practice. Remove it.
FWIW, we used a similar-length grace period (10 years) when dropping
support for history file format version 1 in 77aeb6a2a8 (Port
execution, 2023-10-08).
While at it, rename "input mapping" to "binding", to be consistent
with builtin bind. The "input" bit is already implied by the module
hierarchy.
Also, merge definitions of "enum ReadlineCmd" and
"INPUT_FUNCTION_METADATA" using a macro.
Review with
git show --color-moved=zebra --color-moved-ws=allow-indentation-change
Types like Environment are sometimes imported using their FQN and
sometimes via their re-export in the parent module; it seems better
to have one canonical name. I think we usually use the re-exports
already, so use them always.
It't not as trivial to enforce this in child modules (such as "mod
tests" which) but that's not a big loss.
While at it, make some other consistency improvements to imports.
Commit 77471c500e (path: use `std::io::Error` instead of raw ints,
2026-01-21) inadvertently prints the "Unable to locate config directory"
error when the *data* dir is inaccessible, rather than when the *config*
dir is inaccessible.
Fix that. While at it, rename variables to avoid this issue.
These don't really belong in config.fish. Put them in function files,
like similar wrappers. The redundancy is ugly but if it's a problem
we can use code generation to fix that.