i’ve been using fish shell over zsh recently, and i haven’t wanted to go back for even a second. the only thing that kind of annoyed me was that the ecosystem isn’t as big, so some scripts and plugins that existed for zsh don’t have fish counterparts (or maybe i just didn’t look hard enough, this is a real possibility)
the main thing i ran into this issue with (and something i took for granted) was automatic activation of python environments when you enter a folder. i was worried it was going to be a gigantic pain to fix, but it was actually extremely simple.
to start, the function declaration looks roughly like this:
function __auto_py_venv --on-variable PWD --description "auto enter python venvs"
...
end--on-variable PWD allows us to have the function run automatically whenever $PWD changes (so whenever we switch directories). pretty cool
now, we can test if an activation file exists in the current folder like this:
test -f "$dir/.venv/bin/activate.fish" # -f just means it's a fileand if it exists, we can source it:
function __auto_py_venv --on-variable PWD --description "auto enter python venvs"
if test -f ".venv/bin/activate.fish"
source ".venv/bin/activate.fish"
end
endall done! it was that simple
the next issue i had was deactivating the venv when we leave it. this was as simple as setting a global $__VENV_DIR variable with set -g __VENV_DIR $PWD when successfully activating a venv, and then unsetting it and deactivating the venv if the current directory doesnt match $__VENV_DIR:
function __auto_py_venv --on-variable PWD --description "auto activate python venv"
if set -q __VENV_DIR
and not string match -q "$__VENV_DIR*" $PWD
deactivate
set -e __VENV_DIR # -e for erase
end
if test -f ".venv/bin/activate.fish"
source ".venv/bin/activate.fish"
set -g __VENV_DIR $PWD
break
end
endthe final issue i had was cding too deep, especially since i use zoxide. this function can only match .venv/bin/activate.fish, so if i’m in a subdirectory it won’t work.
the solution for this is to cycle upwards and iterate through each directory up to the root.
we can do this by:
- declare
$diras the current directory - check for
.venv/bin/activate.fish. if it’s there, activate + end the loop - if it’s not there, set
$dirto the parent directory - repeat, as long as
$dir != /
in my function, this looked like so:
function __auto_py_venv --on-variable PWD --description "auto activate python venv"
if set -q __VENV_DIR
and not string match -q "$__VENV_DIR*" $PWD
deactivate
set -e __VENV_DIR
end
set -l dir $PWD # -l for local (meaning function only)
while test $dir != / # if it's the root we don't care
if test -f "$dir/.venv/bin/activate.fish"
source "$dir/.venv/bin/activate.fish"
set -g __VENV_DIR $PWD
break
end
set dir (dirname "$dir") # set $dir to the parent directory
end
end