mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-04-25 20:41:15 -03:00
Given that this curly underline will also be red, it should be widely
understood as error.
Since fish always renders immediately (and synchronously), typing "echo"
will briefly show an intermediate curly line. Maybe fish should redraw
after a timer elapses? This is probably unrelated to this patch.
As mentioned in cc9849c279 (Curly underlines in set_color and fish_color_*,
2025-04-13), there are still some terminals that interpret "\e[4:3m" as
something other than curly underline.
Some of them interpret it as background/foreground color, and some terminal
multiplexers downgrade it to straight underlines (which often happens due
to a false positive). I want to change this in multiplexers where possible
(see https://github.com/orgs/tmux/discussions/4477) but for now, disable
this feature in multiplexers (there are just a handful).
In a few years, those terminals will maybe agree with XTerm. Until then,
use XTGETTCAP as a temporary stepping stone. We could also read the terminfo
database but that will give only very few true positives, and lots of false
negatives. Better implement XTGETTCAP in the relevant terminals.
Note that if the universal variables use the "--track" flag (from the
grandparent commit), then
rm -rf /tmp/newhome
foot -e env $HOME=/tmp/newhome fish
xterm -e env $HOME=/tmp/newhome fish
will "magically work". For foot, $fish_color_error will have --underline=curly.
For xterm, it will not, due to the call to "fish_config theme update".
But of course since it's a universal variable, running fish in xterm
would also downgrade the fish running in other terminals.
Add a "fish_config theme save --yes" flag because "status xtgettcap"
requires stdin to be a terminal. I'd probably drop this requirement, and
make "status xtgettcap" always use fish's stdin and not its own. That'd be
cheating because an external command can't do that but I don't think this
change would be hurting anyone.
463 lines
20 KiB
Fish
463 lines
20 KiB
Fish
# Variables a theme is allowed to set
|
|
set -l theme_var_filter '^fish_(?:pager_)?color.*$'
|
|
|
|
function fish_config --description "Launch fish's web based configuration" \
|
|
--inherit-variable theme_var_filter
|
|
|
|
set -l _flag_track
|
|
argparse h/help track yes -- $argv
|
|
or return
|
|
|
|
if set -q _flag_help
|
|
__fish_print_help fish_config
|
|
return 0
|
|
end
|
|
|
|
set -l cmd $argv[1]
|
|
set -e argv[1]
|
|
|
|
if set -q _flag_track[1] && not { test "$cmd" = theme && test "$argv[1]" = save }
|
|
echo >&2 fish_config: --track: unknown option
|
|
return 1
|
|
end
|
|
if set -q _flag_yes[1] && not { contains "$cmd" prompt theme && test "$argv[1]" = save }
|
|
echo >&2 fish_config: --yes: unknown option
|
|
return 1
|
|
end
|
|
|
|
set -q cmd[1]
|
|
or set cmd browse
|
|
|
|
# The web-based configuration UI
|
|
# Also opened with just `fish_config` or `fish_config browse`.
|
|
if contains -- $cmd browse
|
|
set -lx __fish_bin_dir $__fish_bin_dir
|
|
set -l fish_path (status fish-path)
|
|
and set __fish_bin_dir (path dirname -- $fish_path)
|
|
if set -l python (__fish_anypython)
|
|
set -l mainfile $__fish_data_dir/tools/web_config/webconfig.py
|
|
if not path is -- $mainfile
|
|
if not status list-files tools/web_config &>/dev/null
|
|
echo "Cannot find web configuration tool. Please check your fish installation."
|
|
return 1
|
|
end
|
|
set -l temp (mktemp -d)
|
|
for dir in (status list-files tools/web_config | path dirname | path sort -u)
|
|
mkdir -p $temp/$dir
|
|
or return
|
|
end
|
|
for file in (status list-files tools/web_config)
|
|
status get-file $file >$temp/$file
|
|
or return
|
|
end
|
|
set mainfile $temp/tools/web_config/webconfig.py
|
|
end
|
|
|
|
$python "$mainfile" $argv
|
|
|
|
# If the execution of 'webconfig.py' fails, display python location and return.
|
|
if test $status -ne 0
|
|
echo "Please check if Python has been installed successfully."
|
|
echo "You can find the location of Python by executing the 'command -s $python' command."
|
|
return 1
|
|
end
|
|
|
|
else
|
|
echo (set_color $fish_color_error)Cannot launch the web configuration tool:(set_color normal)
|
|
echo (set_color -o)"fish_config browse"(set_color normal) requires Python.
|
|
echo Installing python will fix this, and also enable completions to be
|
|
echo automatically generated from man pages.\n
|
|
echo To change your prompt, use (set_color -o)"fish_config prompt"(set_color normal) or create a (set_color -o)"fish_prompt"(set_color normal) function.
|
|
echo To list the samples use (set_color -o)"fish_config prompt show"(set_color normal).\n
|
|
|
|
echo You can tweak your colors by setting the (set_color $fish_color_search_match)\$fish_color_\*(set_color normal) variables.
|
|
end
|
|
return 0
|
|
end
|
|
|
|
if not contains -- $cmd prompt theme
|
|
echo No such subcommand: $cmd >&2
|
|
return 1
|
|
end
|
|
|
|
switch $cmd
|
|
case prompt
|
|
# prompt - for prompt switching
|
|
set -l cmd $argv[1]
|
|
set -e argv[1]
|
|
|
|
if contains -- $cmd list; and set -q argv[1]
|
|
echo "Too many arguments" >&2
|
|
return 1
|
|
end
|
|
|
|
set -l prompt_dir $__fish_data_dir/sample_prompts $__fish_data_dir/tools/web_config/sample_prompts
|
|
switch $cmd
|
|
case show
|
|
set -l fish (status fish-path)
|
|
set -l prompts $prompt_dir/$argv.fish
|
|
set -q prompts[1]; or set prompts $prompt_dir/*.fish (status list-files tools/web_config/sample_prompts/ 2>/dev/null)
|
|
for p in $prompts
|
|
if not test -e "$p"
|
|
continue
|
|
end
|
|
set -l promptname (string replace -r '.*/([^/]*).fish$' '$1' $p)
|
|
echo -s (set_color --underline) $promptname (set_color normal)
|
|
$fish -c 'functions -e fish_right_prompt;
|
|
if string match -q "tools/*" -- $argv[1]
|
|
status get-file $argv[1] | source
|
|
else
|
|
source $argv[1]
|
|
end
|
|
false
|
|
fish_prompt
|
|
echo (set_color normal)
|
|
if functions -q fish_right_prompt;
|
|
echo right prompt: (false; fish_right_prompt)
|
|
end' $p
|
|
echo
|
|
end
|
|
case list ''
|
|
files=$prompt_dir/*.theme string replace -r '.*/([^/]*).fish$' '$1' $files (status list-files tools/web_config/sample_prompts/)
|
|
return
|
|
case choose
|
|
if set -q argv[2]
|
|
echo "Too many arguments" >&2
|
|
return 1
|
|
end
|
|
if not set -q argv[1]
|
|
echo "Too few arguments" >&2
|
|
return 1
|
|
end
|
|
|
|
set -l have
|
|
for f in $prompt_dir/$argv[1].fish
|
|
if test -f $f
|
|
source $f
|
|
set have $f
|
|
break
|
|
end
|
|
end
|
|
if not set -q have[1]
|
|
if status list-files tools/web_config/sample_prompts/$argv[1].fish &>/dev/null
|
|
status get-file tools/web_config/sample_prompts/$argv[1].fish | source
|
|
# HACK: `source` gives us a filename of "-", so we check manually if we had a right prompt
|
|
set have ""
|
|
status get-file tools/web_config/sample_prompts/$argv[1].fish | string match -q '*function fish_right_prompt*'
|
|
and set have -
|
|
else
|
|
echo "No such prompt: '$argv[1]'" >&2
|
|
return 1
|
|
end
|
|
end
|
|
|
|
# Erase the right prompt if it didn't have any.
|
|
if functions -q fish_right_prompt; and test (functions --details fish_right_prompt) != $have[1]
|
|
functions --erase fish_right_prompt
|
|
end
|
|
case save
|
|
if set -q _flag_yes[1] ||
|
|
{ read -P"Overwrite prompt? [y/N]" -l yesno; string match -riq 'y(es)?' -- $yesno }
|
|
echo Overwriting
|
|
# Skip the cp if unnecessary,
|
|
# or we'd throw an error on a stock fish.
|
|
path is $__fish_config_dir/functions/fish_prompt.fish
|
|
and cp $__fish_config_dir/functions/fish_prompt.fish{,.bak}
|
|
path is $__fish_config_dir/functions/fish_right_prompt.fish
|
|
and cp $__fish_config_dir/functions/fish_right_prompt.fish{,.bak}
|
|
|
|
set -l have
|
|
if set -q argv[1]
|
|
for f in $prompt_dir/$argv[1].fish
|
|
if test -f $f
|
|
set have $f
|
|
# Set the functions to empty so we empty the file
|
|
# if necessary.
|
|
function fish_prompt
|
|
end
|
|
function fish_right_prompt
|
|
end
|
|
source $f
|
|
or return 2
|
|
end
|
|
end
|
|
if not set -q have[1]
|
|
if status list-files tools/web_config/sample_prompts/$argv[1].fish &>/dev/null
|
|
status get-file tools/web_config/sample_prompts/$argv[1].fish | source
|
|
else
|
|
echo "No such prompt: '$argv[1]'" >&2
|
|
return 1
|
|
end
|
|
end
|
|
end
|
|
|
|
funcsave fish_prompt
|
|
or return
|
|
|
|
funcsave fish_right_prompt 2>/dev/null
|
|
return
|
|
else
|
|
echo Not overwriting
|
|
return 1
|
|
end
|
|
end
|
|
|
|
return 0
|
|
case theme
|
|
# Selecting themes
|
|
set -l cmd $argv[1]
|
|
set -e argv[1]
|
|
|
|
if contains -- $cmd list; and set -q argv[1]
|
|
echo "Too many arguments" >&2
|
|
return 1
|
|
end
|
|
|
|
set -l dirs $__fish_config_dir/themes $__fish_data_dir/tools/web_config/themes
|
|
|
|
switch $cmd
|
|
case list ''
|
|
files=$dirs/*.theme string replace -r '.*/([^/]*).theme$' '$1' $files (status list-files tools/web_config/themes/)
|
|
return
|
|
case demo
|
|
echo -ns (set_color $fish_color_command || set_color $fish_color_normal) /bright/vixens
|
|
echo -ns (set_color normal) ' '
|
|
echo -ns (set_color $fish_color_param || set_color $fish_color_normal) jump
|
|
echo -ns (set_color normal) ' '
|
|
echo -ns (set_color $fish_color_redirection || set_color $fish_color_normal) '|'
|
|
echo -ns (set_color normal) ' '
|
|
echo -ns (set_color $fish_color_quote || set_color $fish_color_normal) '"fowl"'
|
|
echo -ns (set_color normal) ' '
|
|
echo -ns (set_color $fish_color_redirection || set_color $fish_color_normal) '> quack'
|
|
echo -ns (set_color normal) ' '
|
|
echo -ns (set_color $fish_color_end || set_color $fish_color_normal) '&'
|
|
set_color normal
|
|
echo -s (set_color $fish_color_comment || set_color $fish_color_normal) ' # This is a comment'
|
|
set_color normal
|
|
echo -ns (set_color $fish_color_command || set_color $fish_color_normal) echo
|
|
echo -ns (set_color normal) ' '
|
|
echo -s (set_color $fish_color_error || set_color $fish_color_normal) "'" (set_color $fish_color_quote || set_color $fish_color_normal) "Errors are the portal to discovery"
|
|
set_color normal
|
|
echo -ns (set_color $fish_color_command || set_color $fish_color_normal) Th
|
|
set_color normal
|
|
set_color $fish_color_autosuggestion || set_color $fish_color_normal
|
|
echo is an autosuggestion
|
|
echo
|
|
case show
|
|
set -l fish (status fish-path)
|
|
set -l themes $dirs/$argv.theme (status list-files tools/web_config/themes/ | string match -- "*/"$argv.theme)
|
|
set -q themes[1]; or set themes $dirs/*.theme (status list-files tools/web_config/themes/)
|
|
set -l used_themes
|
|
|
|
echo -s (set_color normal; set_color --underline) Current (set_color normal)
|
|
fish_config theme demo
|
|
|
|
for t in $themes
|
|
not test -e "$t"
|
|
and continue
|
|
|
|
set -l themename (string replace -r '.*/([^/]*).theme$' '$1' $t)
|
|
contains -- $themename $used_themes
|
|
and continue
|
|
set -a used_themes $themename
|
|
|
|
echo -s (set_color normal; set_color --underline) $themename (set_color normal)
|
|
|
|
# Use a new, --no-config, fish to display the theme.
|
|
# So we can use this function, explicitly source it before anything else!
|
|
functions fish_config | $fish -C "source -" --no-config -c '
|
|
fish_config theme choose $argv
|
|
fish_config theme demo $argv
|
|
' $themename
|
|
end
|
|
|
|
case choose save
|
|
if set -q argv[2]
|
|
echo "Too many arguments" >&2
|
|
return 1
|
|
end
|
|
# The name of the theme to save *from* is optional for `fish_config theme save`
|
|
if not set -q argv[1] && contains -- $cmd choose
|
|
echo "Too few arguments" >&2
|
|
return 1
|
|
end
|
|
|
|
set -l scope -g
|
|
set -l have_colors
|
|
|
|
if contains -- $cmd save
|
|
if not set -q _flag_yes &&
|
|
{ read -P"Overwrite your current theme? [y/N] " -l yesno
|
|
not string match -riq 'y(es)?' -- $yesno
|
|
}
|
|
echo Not overwriting >&2
|
|
return 1
|
|
end
|
|
set scope -U
|
|
end
|
|
|
|
set -l known_colors fish_color_{normal,command,keyword,quote,redirection,\
|
|
end,error,param,option,comment,selection,operator,escape,autosuggestion,\
|
|
cwd,user,host,host_remote,cancel,search_match} \
|
|
fish_pager_color_{progress,background,prefix,completion,description,\
|
|
selected_background,selected_prefix,selected_completion,selected_description,\
|
|
secondary_background,secondary_prefix,secondary_completion,secondary_description}
|
|
|
|
# If we are choosing a theme or saving from a named theme, load the theme now.
|
|
# Otherwise, we'll persist the currently loaded/themed variables (in case of `theme save`).
|
|
if set -q argv[1]
|
|
__fish_config_theme_get $argv[1] | while read -lat toks
|
|
# The whitelist allows only color variables.
|
|
# Not the specific list, but something named *like* a color variable.
|
|
# This also takes care of empty lines and comment lines.
|
|
string match -rq -- $theme_var_filter $toks[1]
|
|
or continue
|
|
|
|
# If we're supposed to set universally, remove any shadowing globals
|
|
# so the change takes effect immediately (and there's no warning).
|
|
if test x"$scope" = x-U; and set -qg $toks[1]
|
|
set -eg $toks[1]
|
|
end
|
|
set $scope $toks $_flag_track=$argv[1]
|
|
set -a have_colors $toks[1]
|
|
end
|
|
|
|
# Set all colors that aren't mentioned to empty
|
|
for c in $known_colors
|
|
contains -- $c $have_colors
|
|
and continue
|
|
|
|
# Erase conflicting global variables so we don't get a warning and
|
|
# so changes are observed immediately.
|
|
set -eg $c
|
|
set $scope $c $_flag_track=$argv[1]
|
|
end
|
|
else
|
|
# We're persisting whatever current colors are loaded (maybe in the global scope)
|
|
# to the universal scope, without overriding them from a theme file.
|
|
# Like above, make sure to erase from other scopes first and ensure known color
|
|
# variables are defined, even if empty.
|
|
# This branch is only reachable in the case of `theme save` so $scope is always `-U`.
|
|
|
|
for color in (printf "%s\n" $known_colors (set --names | string match -r $theme_var_filter) | sort -u)
|
|
if set -q $color
|
|
# Cache the value from whatever scope currently defines it
|
|
set -l value $$color
|
|
set -eg $color
|
|
set -U $color $value
|
|
end
|
|
end
|
|
end
|
|
|
|
# If we've made it this far, we've either found a theme file or persisted the current
|
|
# state (if any). In all cases we haven't failed, so return 0.
|
|
return 0
|
|
case update
|
|
__fish_config_theme_update $argv
|
|
case dump
|
|
# Write the current theme in .theme format, to stdout.
|
|
set -L | string match -r $theme_var_filter
|
|
case '*'
|
|
echo "No such command: $cmd" >&2
|
|
return 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function __fish_config_theme_get
|
|
set -l dirs $__fish_config_dir/themes $__fish_data_dir/tools/web_config/themes
|
|
set -l files $dirs/$argv[1].theme
|
|
set -l file
|
|
set -l is_default_theme false
|
|
|
|
for f in $files
|
|
if test -e "$f"
|
|
set file $f
|
|
if test $f = $__fish_data_dir/tools/web_config/themes/$argv[1].theme
|
|
set is_default_theme true
|
|
end
|
|
break
|
|
end
|
|
end
|
|
|
|
if not set -q file[1]
|
|
if status list-files tools/web_config/themes/$argv[1].theme &>/dev/null
|
|
set file tools/web_config/themes/$argv[1].theme
|
|
set is_default_theme true
|
|
else
|
|
echo "No such theme: $argv[1]" >&2
|
|
echo "Searched directories: $dirs" >&2
|
|
return 1
|
|
end
|
|
end
|
|
|
|
set -l content (
|
|
if string match -qr '^tools/' -- $file
|
|
status get-file $file
|
|
else
|
|
string join \n <$file
|
|
end
|
|
)
|
|
if $is_default_theme && not __fish_in_gnu_screen && not __fish_xtgettcap Su && not __fish_in_terminal_multiplexer
|
|
set content (string replace -- --underline=curly "" $content)
|
|
end
|
|
|
|
string join \n $content
|
|
end
|
|
|
|
function __fish_config_show_tracked_color_vars
|
|
set -l color_var $argv[1]
|
|
set -l _flag_track
|
|
argparse --ignore-unknown track= -- _set_color $argv[2..]
|
|
or return $status
|
|
if not set -q _flag_track[1]
|
|
return
|
|
end
|
|
if set -q _flag_track[2]
|
|
echo >&2 "fish_config: $color_var: --track option can only be specified once"
|
|
exit 1
|
|
end
|
|
if test (printf %s $_flag_track | count) -ne 0
|
|
echo >&2 "fish_config: $color_var: error: tracking theme name must not contain newlines"
|
|
exit 1
|
|
end
|
|
printf %s\n $color_var $_flag_track
|
|
end
|
|
|
|
function __fish_config_theme_update --inherit-variable theme_var_filter
|
|
if set -q argv[1]
|
|
echo "fish_config: too many arguments" >&2
|
|
return 1
|
|
end
|
|
set -l themes
|
|
|
|
set -l tracking_variables (
|
|
set --universal --long |
|
|
string match -r '^fish_(?:pager_)?color.*$' |
|
|
string replace -r '.*' '__fish_config_show_tracked_color_vars $0' |
|
|
source
|
|
)
|
|
or return $status
|
|
string join \n $tracking_variables |
|
|
while read --line _colorvar theme
|
|
if not contains -- $theme $themes
|
|
set -a themes $theme
|
|
end
|
|
end
|
|
|
|
for theme in $themes
|
|
set -l colorvars
|
|
string join \n $tracking_variables |
|
|
while read --line color_var t
|
|
if test $t = $theme
|
|
set -a colorvars $color_var
|
|
end
|
|
end
|
|
set -l theme_escaped (string escape -- $theme)
|
|
__fish_config_theme_get $theme |
|
|
string match -r -- "^(?:$(string join '|' $colorvars))\b .*" |
|
|
string replace -r '.*' "set -U \$0 --track=$theme_escaped" |
|
|
source
|
|
end
|
|
end
|