Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fish_command_not_found is undefined on macOS #7777

Closed
gpanders opened this issue Mar 4, 2021 · 7 comments
Closed

fish_command_not_found is undefined on macOS #7777

gpanders opened this issue Mar 4, 2021 · 7 comments

Comments

@gpanders
Copy link
Contributor

gpanders commented Mar 4, 2021

Hello!

The new fish_command_not_found function in fish 3.2.0 doesn't seem to have a "default" value if none of the if branches match:

fish_command_not_found.fish:

# If an old handler already exists, defer to that.
if functions -q __fish_command_not_found_handler
    function fish_command_not_found
        # The fish_command_not_found event was removed in fish 3.2.0,
        # and future versions of fish will just call a function called "fish_command_not_found".
        # You have defined a custom handler, we suggest renaming it to "fish_command_not_found".
        __fish_command_not_found_handler $argv
    end
    # First check if we are on OpenSUSE since SUSE's handler has no options
    # but the same name and path as Ubuntu's.
else if contains -- suse $os || contains -- sles $os && type -q command-not-found
    # ...
    # Lots of 'else if' heuristics for different OS's
    # ...
else if type -q pacman
    function fish_command_not_found
        set -l paths $argv[1]
        # If we've not been given an absolute path, try $PATH as the starting point,
        # otherwise pacman will try *every path*, and e.g. bash-completion
        # isn't helpful.
        string match -q '/*' -- $argv[1]; or set paths $PATH/$argv[1]
        # Pacman only prints the path, so we still need to print the error.
        __fish_default_command_not_found_handler $argv[1]
        pacman -F $paths
    end
end

If none of these conditions match (which they don't on e.g. macOS), then fish_command_not_found doesn't work:

$ fish_command_not_found hi
fish: Unknown command: fish_command_not_found

(The irony of the 'Unknown command' for fish_command_not_found is amusing).

I would expect the fish_command_not_found function file to end with this:

...
else if type -q pacman
    function fish_command_not_found
        set -l paths $argv[1]
        # If we've not been given an absolute path, try $PATH as the starting point,
        # otherwise pacman will try *every path*, and e.g. bash-completion
        # isn't helpful.
        string match -q '/*' -- $argv[1]; or set paths $PATH/$argv[1]
        # Pacman only prints the path, so we still need to print the error.
        __fish_default_command_not_found_handler $argv[1]
        pacman -F $paths
    end
else
    function fish_command_not_found
        __fish_default_command_not_found_handler $argv[1]
    end
end

Is there a reason it's not done this way?

If it's helpful, I can provide the use case that led me to this problem. I have some functions defined that lazy-load other programs (i.e. ones that need to initialize the shell in some way before they can work properly, but I don't want to pay that initialization cost on every shell, just when I need to use it).

Example:

function conda -d "Lazy load conda"
    functions -e conda
    if command -sq conda
        source (command conda info --root)/etc/fish/conf.d/conda.fish
        conda $argv
    else
        if functions -q fish_command_not_found
            fish_command_not_found conda
        else
            __fish_default_command_not_found_handler conda
        end
        return 127
    end
end

If command -sq conda is false, I'd like to be able to just call fish_command_not_found conda, but this doesn't work for the reasons I pointed out above. Instead, I have to check for the existence of fish_command_not_found, and if it doesn't exist, fallback to __fish_default_command_not_found_handler, which is ugly, redundant, and relies on fish internals instead of using the proper abstractions.

@faho faho closed this as completed in b1c5e00 Mar 4, 2021
@faho
Copy link
Member

faho commented Mar 4, 2021

Is there a reason it's not done this way?

Yes: I overlooked it.

@faho faho added the regression Something that used to work, but was broken, especially between releases label Mar 4, 2021
@faho faho added this to the fish 3.2.1 milestone Mar 4, 2021
@faho faho added enhancement and removed regression Something that used to work, but was broken, especially between releases labels Mar 8, 2021
@rdw20170120
Copy link

Is this fixed and released? I just started using Fish, with 3.3.1, and I seem to be encountering these symptoms. I created a "fish_command_not_found" function, but it seems to never be called. I can call it manually and it works. But when my Fish script contains an invalid command, I get a default handler of some kind but not a call to mine.

@rdw20170120
Copy link

Sorry, it seems to be a bit more complicated than that, but mostly regarding my own understanding. Fish is probably working just fine.

When I open a new Fish shell, then attempt a nonexistent command, I get proper default behavior.

I then run a script that customizes Fish a little for my development project. It adds a project directory to fish_function_path, and it sets a new PATH. This allows Fish to find my custom functions and my custom scripts specific to my project (within a git working directory).

Now when I attempt a nonexistent command, Fish appears to invoke my custom fish_command_not_found function. That function writes its custom output. Then the Fish shell exits. For example:

rob@Heka:~/repo/GitLab/WIP/private-mono$ fish
Welcome to fish, the friendly interactive shell
Type help for instructions on how to use fish
rob@Heka ~/r/G/W/private-mono (main)> hello
fish: Unknown command: hello
rob@Heka ~/r/G/W/private-mono (main) [127]> . activate.fish
Running Fish as /usr/local/bin/fish
Executing /Users/rob/repo/GitLab/WIP/private-mono/activate.fish
Activated.
Hello, world!
Done.
rob@Heka ~/r/G/W/private-mono (main)> hello
ERROR: Command NOT found: hello
in function 'fish_command_not_found' with arguments 'hello'
in event handler: handler for generic event 'fish_command_not_found'
rob@Heka:~/repo/GitLab/WIP/private-mono$

Here is the function:

function fish_command_not_found \
    --description 'Called by Fish when a command is not found'
    echo ERROR: Command NOT found: $argv[1]
    status --print-stack-trace
    exit 97
end

My desired behavior is to stop the execution of my script containing the nonexistent command, while printing a stack trace to show me where it happened. However, it seems that my function is running inside the Fish "plumbing", so my exit is exiting both my script and the Fish shell (oops!).

So, apparently I do not understand something crucial to making this work properly.

Does anyone have any insights or suggestions?

@rdw20170120
Copy link

Ah, I learned (was reminded of) a couple of things. I added the link to the event: --on-event fish_command_not_found. And I remembered that I must source the file to get the function defined and connected to the event.

I commented out the exit 97, so now my function does not exit the Fish shell BUT it also does not stop my script from executing past the nonexistent command. How do I accomplish that?

@faho
Copy link
Member

faho commented Jul 20, 2021

Is this fixed and released?

@rdw20170120

This is, yes. That's why it was closed in a commit, and the commit is included in fish 3.2.1, as you can see by the milestone we linked it to.

However, what you're asking about is a different thing, which is why it would have been better if you had opened a new issue. Oh well.

My desired behavior is to stop the execution of my script containing the nonexistent command, while printing a stack trace to show me where it happened.

This is not something the command not found handler is made for.

I commented out the exit 97, so now my function does not exit the Fish shell BUT it also does not stop my script from executing past the nonexistent command. How do I accomplish that?

Now, if you're actually executing a script with fish /path/to/script or source /path/to/script, you can use exit. If you are executing a function inside the currently running fish, you'll have to check if a function exists before.

I added the link to the event: --on-event fish_command_not_found.

The event doesn't exist anymore in fish > 3.2, so this does nothing.

And I remembered that I must source the file to get the function defined and connected to the event.

If the function is in $fish_function_path, you don't have to source it explicitly, as long as it's named fish_command_not_found.fish. That's what $fish_function_path is for.

Now you used to have to source this particular one to get the event connected, but that's one of the reasons why it's not a named event anymore.

@rdw20170120
Copy link

Ah, thanks for clarifying.

I found mention that the event had been removed, but I thought that I was somehow mistaken. Now I am straight on that.

It is apparent that my handler does not get invoked when I use a nonexistent command in a new shell. This makes sense to me, because none of the functions in $fish_function_path get autoloaded until the first time they are invoked. It only gets invoked once I have invoked it manually (but I would prefer not to do so).

It appears that the common convention is to define such a function during my Fish startup. However, that does not fit my situation since this is functionality specific to a particular project (git repository). So I am forced to source the function definition. That works fine in my situation.

I am still left with my basic issue of how to stop the script without exiting the shell. I will take your suggestion and open a separate issue. Thanks again for the response.

@rdw20170120
Copy link

Opened new issue #8155

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 25, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants