Files
fish-shell/share/functions/fish_config.fish
Johannes Altmanninger ba2ad33a81 Use curly underlines for errors in default themes
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.
2025-04-29 13:59:27 +02:00

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