From 054f9baf88a72383a15259d10098e5653b1a8a32 Mon Sep 17 00:00:00 2001 From: Fabian Boehm Date: Sat, 1 Oct 2022 12:44:32 +0200 Subject: [PATCH] Add a fish_delta helper function This helps figuring out which functions, completions and config you've overridden. --- doc_src/cmds/fish_delta.rst | 81 ++++++++++++++++ share/completions/fish_delta.fish | 9 ++ share/functions/fish_delta.fish | 156 ++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 doc_src/cmds/fish_delta.rst create mode 100644 share/completions/fish_delta.fish create mode 100644 share/functions/fish_delta.fish diff --git a/doc_src/cmds/fish_delta.rst b/doc_src/cmds/fish_delta.rst new file mode 100644 index 000000000..db49e7521 --- /dev/null +++ b/doc_src/cmds/fish_delta.rst @@ -0,0 +1,81 @@ +fish_delta - compare functions and completions to the default +============================================================== + +Synopsis +-------- + +.. synopsis:: + + fish_delta name ... + fish_delta [-f | --no-functions] [-c | --no-completions] [-C | --no-config] [-d | --no-diff] [-n | --new] [-V | --vendor=] + fish_delta [-h | --help] + + +Description +----------- + +The ``fish_delta`` function tells you, at a glance, which of your functions and completions differ from the set that fish ships. + +It does this by going through the relevant variables (:envvar:`fish_function_path` for functions and :envvar:`fish_complete_path` for completions) and comparing the files against fish's default directories. + +If any names are given, it will only compare files by those names (plus a ".fish" extension). + +By default, it will also use ``diff`` to display the difference between the files. If ``diff`` is unavailable, it will skip it, but in that case it also cannot figure out if the files really differ. + +The exit status is 1 if there was a difference and 2 for other errors, otherwise 0. + +Options +------- + +The following options are available: + +**-f** or **--no-functions** + Stops checking functions + +**-c** or **--no-completions** + Stops checking completions + +**-C** or **--no-config** + Stops checking configuration files like config.fish or snippets in the conf.d directories. + +**-d** or **--no-diff** + Removes the diff display (this happens automatically if ``diff`` can't be found) + +**-n** or **--new** + Also prints new files (i.e. those that can't be found in fish's default directories). + +**-Vvalue** or **--vendor=value** + Determines how the vendor directories are counted. Valid values are: + + - "default" - counts vendor files as belonging to the defaults. Any changes in other directories will be counted as changes over them. This is the default. + - "user" - counts vendor files as belonging to the user files. Any changes in them will be counted as new or changed files. + - "ignore" - ignores vendor directories. Files of the same name will be counted as "new" if no file of the same name in fish's default directories exists. + +**-h** or **--help** + Prints ``fish_delta``'s help (this). + +Example +------- + +Running just:: + + fish_delta + +will give you a list of all your changed functions and completions, including diffs (if you have the ``diff`` command). + +It might look like this:: + + > fish_delta + New: /home/alfa/.config/fish/functions/battery.fish + Changed: /home/alfa/.config/fish/test/completions/cargo.fish + --- /home/alfa/.config/fish/test/completions/cargo.fish 2022-09-02 12:57:55.579229959 +0200 + +++ /usr/share/fish/completions/cargo.fish 2022-09-25 17:51:53.000000000 +0200 + # the output of `diff` follows + +The options are there to select which parts of the output you want. With ``--no-completions`` you can compare just functions, and with ``--no-diff`` you can turn off the ``diff`` display. + +To only compare your ``fish_git_prompt``, you might use:: + + fish_delta --no-completions fish_git_prompt + +which will only compare files called "fish_git_prompt.fish". diff --git a/share/completions/fish_delta.fish b/share/completions/fish_delta.fish new file mode 100644 index 000000000..51c3ea1fa --- /dev/null +++ b/share/completions/fish_delta.fish @@ -0,0 +1,9 @@ +complete -c fish_delta -f -a '(path filter -rf -- $fish_function_path/*.fish $fish_complete_path/*.fish $__fish_vendor_confdirs/*.fish | +path basename | path change-extension "") config' + +complete -c fish_delta -s c -l no-completions -d 'Ignore completions' +complete -c fish_delta -s f -l no-functions -d 'Ignore function files' +complete -c fish_delta -s C -l no-config -d 'Ignore config files' +complete -c fish_delta -s d -l no-diff -d 'Don\'t display the full diff' +complete -c fish_delta -s n -l new -d 'Include new files' +complete -c fish_delta -s V -l vendor -d 'Choose how to count vendor files' -xa 'ignore\t"Skip vendor files" user\t"Count vendor files as belonging to the user" default\t"Count vendor files as fish defaults"' diff --git a/share/functions/fish_delta.fish b/share/functions/fish_delta.fish new file mode 100644 index 000000000..75220f4f5 --- /dev/null +++ b/share/functions/fish_delta.fish @@ -0,0 +1,156 @@ +function fish_delta + argparse h/help f/no-functions c/no-completions C/no-config d/no-diff n/new V/vendor= -- $argv + + if set -q _flag_help + __fish_print_help fish_delta + return 0 + end + + type -q diff + or set -l _flag_d --no-diff + + set -l vendormode default + if set -ql _flag_vendor[1] + if not contains -- $_flag_vendor[-1] default ignore user + echo "Wrong vendor mode '$_flag_vendor[-1]'." >&2 + echo "Valid values are: default, ignore, new" >&2 + return 2 + end + set vendormode $_flag_vendor[-1] + end + + # TODO: Do we want to keep the vendor dirs in here? + set -l default_function_path $__fish_data_dir/functions + test "$vendormode" = default && set -a default_function_path $__fish_vendor_functionsdirs + + set -l default_complete_path $__fish_data_dir/completions + test "$vendormode" = default && set -a default_completions_path $__fish_vendor_completionsdirs + + set -l default_conf_path + test "$vendormode" = default && set -a default_conf_path $__fish_vendor_confdirs + + set -l user_function_path + set -l user_complete_path + set -l user_conf_path $__fish_config_dir/conf.d $__fish_sysconf_dir/conf.d + test "$vendormode" = user && set -a user_conf_path $__fish_vendor_confdirs + + for dir in $fish_function_path + if contains -- $vendormode ignore default + contains -- $dir $__fish_vendor_functionsdirs + and continue + end + contains -- $dir $default_function_path + or set -a user_function_path $dir + end + for dir in $fish_complete_path + if contains -- $vendormode ignore default + contains -- $dir $__fish_vendor_completionsdirs + and continue + end + # We don't care about generated completions. + # They shouldn't be compared at all. + contains -- $dir $default_complete_path $__fish_user_data_dir/generated_completions + or set -a user_complete_path $dir + end + + set -l vars + not set -ql _flag_f[1] + and set -a vars user_function_path default_function_path + not set -ql _flag_c[1] + and set -a vars user_complete_path default_complete_path + not set -ql _flag_C[1] + and set -a vars user_conf_path default_conf_path + + if not set -q vars[1] + echo No directories to check! >&2 + return 2 + end + + set -l have_diff 0 + + if isatty stdout + set -f colors "$(set_color normal)" "$(set_color brblue)" "$(set_color bryellow)" "$(set_color green)" "$(set_color red)" + set -f pager less + set -q PAGER + and echo $PAGER | read -at pager + if type -q less + set -l lessopts isRF + if test (less --version | string match -r 'less (\d+)')[2] -lt 530 2>/dev/null + set lessopts "$lessopts"X + end + + not set -qx LESS + and set -xf LESS $lessopts + end + else + set -f colors "" "" "" "" "" + set -f pager cat # cannot use a builtin here + end + + begin + while set -q vars[1] + set -l user_var $$vars[1] + set -l default_var $$vars[2] + set -l all_changed 0 + # We count config files as "changed" compared to /dev/null + # because they are being run. + test "$vars[1]" = user_conf_path + and set all_changed 1 + set -e vars[..2] + set -l files (path filter -rf -- $user_var/$argv.fish) + set -q argv[1] + or set files (path filter -rf -- $user_var/*) + set -q files[1] + and set have_diff 1 + + for file in $files + set -l bn (path basename -- $file) + set -l def (path filter -rf -- $default_var/$bn)[1] + or begin + if test $all_changed = 0 + set -ql _flag_n + and printf (_ "%sNew%s: %s\n") $colors[2] $colors[1] $file + continue + else + set def /dev/null + end + end + + if type -q diff + # We execute diff twice - once to figure out if it's changed, + # so we can get nicer output. + # + if not diff -q -- $file $def >/dev/null 2>&1 + printf (_ "%sChanged%s: %s\n") $colors[3] $colors[1] $file + not set -ql _flag_d[1] + and diff -u -- $def $file + else + printf (_ "%sUnmodified%s: %s\n") $colors[4] $colors[1] $file + end + else + # Without diff, we can't really tell if the contents are the same. + printf (_ "%sPossibly changed%s: %s\n") $colors[3] $colors[1] $file + end + end + end + # config.fish is special - it's a single file, not a directory, + # and it's not precedenced - *both* ~/.config/fish/config.fish and /etc/fish/config.fish + # are executed. + if not set -ql _flag_C[1]; and begin + not set -q argv[1] + or contains -- config $argv + end + for file in (path filter -rf -- $__fish_sysconf_dir/config.fish $__fish_config_dir/config.fish) + # We count these as "changed" so they show up. + printf (_ "%sChanged%s: %s\n") $colors[3] $colors[1] $file + not set -ql _flag_d[1] + and diff -u -- /dev/null $file + end + end + # Instead of using diff's --color=always, just do the coloring ourselves. + end | string replace -r -- '^(-.*)$' "$colors[5]\$1$colors[1]" | + string replace -r -- '^(\+.*)$' "$colors[4]\$1$colors[1]" | + $pager + + return $have_diff +end