Call "fish_command_not_found" if a command wasn't found

Previously, when a command wasn't found, fish would emit the
"fish_command_not_found" *event*.

This was annoying as it was hard to override (the code ended up
checking for a function called `__fish_command_not_found_handler`
anyway!), the setup was ugly,
and it's useless - there is no use case for multiple command-not-found handlers.

Instead, let's just call a function `fish_command_not_found` if it
exists, or print the default message otherwise.

The event is completely removed, but because a missing event is not an error
(MEISNAE in C++-speak) this isn't an issue.

Note that, for backwards-compatibility, we still keep the default
handler function around even tho the new one is hard-coded in C++.

Also, if we detect a previous handler, the new handler just calls it.

This way, the backwards-compatible way to install a custom handler is:

```fish
function __fish_command_not_found_handler --on-event fish_command_not_found
    # do a little dance, make a little love, get down tonight
end
```

and the new hotness is

```fish
function fish_command_not_found
    # do the thing
end
```

Fixes #7293.
This commit is contained in:
Fabian Homborg
2020-08-29 21:54:13 +02:00
parent d1dab22691
commit 340de73172
7 changed files with 199 additions and 65 deletions

View File

@@ -749,10 +749,39 @@ end_execution_reason_t parse_execution_context_t::handle_command_not_found(
event_args.insert(event_args.begin(), cmd_str);
}
event_fire_generic(*parser, L"fish_command_not_found", &event_args);
wcstring buffer;
wcstring error;
// Here we want to report an error (so it shows a backtrace), but with no text.
return this->report_error(STATUS_CMD_UNKNOWN, statement, L"");
// Redirect to stderr
auto io = io_chain_t{};
io.append_from_specs({redirection_spec_t{STDOUT_FILENO, redirection_mode_t::fd, L"2"}}, L"");
if (function_exists(L"fish_command_not_found", *parser)) {
buffer = L"fish_command_not_found";
for (const wcstring &arg : event_args) {
buffer.push_back(L' ');
buffer.append(escape_string(arg, ESCAPE_ALL));
}
auto prev_statuses = parser->get_last_statuses();
event_t event(event_type_t::generic);
event.desc.str_param1 = L"fish_command_not_found";
block_t *b = parser->push_block(block_t::event_block(event));
parser->eval(buffer, io);
parser->pop_block(b);
parser->set_last_statuses(std::move(prev_statuses));
} else {
// If we have no handler, just print it as a normal error.
error = _(L"Unknown command:");
if (!event_args.empty()) {
error.push_back(L' ');
error.append(escape_string(event_args[0], ESCAPE_ALL));
}
}
// Here we want to report an error (so it shows a backtrace).
// If the handler printed text, that's already shown, so error will be empty.
return this->report_error(STATUS_CMD_UNKNOWN, statement, error.c_str());
}
}