diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f62368b04..e15cfab30 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -91,6 +91,7 @@ Scripting improvements - ``abbr --erase`` no longer errors on an unset abbreviation (#7376). - ``test -t``, for testing whether file descriptors are connected to a terminal, works for file descriptors 0, 1, and 2 (#4766). It can still return incorrect results in other cases (#1228). - Trying to run fish scripts with Windows line endings (CRLF) produces a sensible error (#2783). +- An ``alias`` that delegates to a command with the same name no longer triggers an error about recursive completion (#7389). Interactive improvements ------------------------ diff --git a/share/functions/alias.fish b/share/functions/alias.fish index 352ae3a98..0722ae5fa 100644 --- a/share/functions/alias.fish +++ b/share/functions/alias.fish @@ -11,8 +11,6 @@ function alias --description 'Creates a function wrapping a command' set -l name set -l body set -l prefix - set -l first_word - set -l wrapped_cmd if not set -q argv[1] # Print the known aliases. @@ -46,7 +44,8 @@ function alias --description 'Creates a function wrapping a command' # Extract the first command from the body. printf '%s\n' $body | read -l --list words - set first_word $words[1] + set -l first_word $words[1] + set -l last_word $words[-1] # Prevent the alias from immediately running into an infinite recursion if # $body starts with the same command as $name. @@ -59,8 +58,16 @@ function alias --description 'Creates a function wrapping a command' end set -l cmd_string (string escape -- "alias $argv") - set wrapped_cmd (string escape -- $body) - echo "function $name --wraps $wrapped_cmd --description $cmd_string; $prefix $body \$argv; end" | source + # Do not define wrapper completion if we have "alias foo 'foo xyz'" or "alias foo 'sudo foo'" + # This is to prevent completions from recursively calling themselves (#7389). + # The latter will have rare false positives but it's more important to + # prevent recursion for this high-level command. + set -l wraps + if test $first_word != $name; and test $last_word != $name + set wraps --wraps (string escape -- $body) + end + + echo "function $name $wraps --description $cmd_string; $prefix $body \$argv; end" | source if set -q _flag_save funcsave $name end diff --git a/tests/checks/alias.fish b/tests/checks/alias.fish index ef61d3918..4cbb2c2b0 100644 --- a/tests/checks/alias.fish +++ b/tests/checks/alias.fish @@ -31,3 +31,19 @@ functions d # CHECK: function d --wraps=\'/mnt/c/Program\ Files\ \(x86\)/devenv.exe\'\ /Edit --description alias\ d\ \'/mnt/c/Program\ Files\ \(x86\)/devenv.exe\'\ /Edit # CHECK: '/mnt/c/Program Files (x86)/devenv.exe' /Edit $argv; # CHECK: end + +# Use "command" to prevent recusion, and don't add --wraps to avoid accidental recursion in completion. +alias e 'e --option=value' +functions e +# CHECK: # Defined in - @ line 1 +# CHECK: function e --description 'alias e e --option=value' +# CHECK: command e --option=value $argv; +# CHECK: end + +# Don't add --wraps if it looks like a wrapper command to avoid accidental recursion in completion. +alias f 'wrapper around f' +functions f +# CHECK: # Defined in - @ line 1 +# CHECK: function f --description 'alias f wrapper around f' +# CHECK: wrapper around f $argv; +# CHECK: end