Repository: sxyazi/yazi (language: rust)

Issue:
Give users a prompt if they don't have fzf installed (fundamentally important for zoxide integration)
### `yazi --debug` output

```Shell
Yazi
    Version: 0.3.3 (7c445ce 2024-09-04)
    Debug  : false
    OS     : linux-x86_64 (unix)

Ya
    Version: 0.3.3 (7c445ce 2024-09-04)

Emulator
    Emulator.via_env: ("xterm-256color", "WezTerm")
    Emulator.via_csi: Ok(Unknown([Sixel]))
    Emulator.detect : WezTerm

Adapter
    Adapter.matches: Sixel

Desktop
    XDG_SESSION_TYPE           : Some("wayland")
    WAYLAND_DISPLAY            : Some("wayland-1")
    DISPLAY                    : Some(":1")
    SWAYSOCK                   : None
    HYPRLAND_INSTANCE_SIGNATURE: None
    WAYFIRE_SOCKET             : None

SSH
    shared.in_ssh_connection: false

WSL
    WSL: false

Variables
    SHELL              : Some("/home/lucca/.cargo/bin/nu")
    EDITOR             : Some("/home/lucca/user_installs/helix/target/release/hx")
    VISUAL             : None
    YAZI_FILE_ONE      : None
    YAZI_CONFIG_HOME   : None

Text Opener
    default: Some(Opener { run: "${EDITOR:-vi} \"$@\"", block: true, orphan: false, desc: "$EDITOR", for_: None, spread: true })
    block  : Some(Opener { run: "${EDITOR:-vi} \"$@\"", block: true, orphan: false, desc: "$EDITOR", for_: None, spread: true })

Multiplexers
    TMUX               : false
    tmux version       : No such file or directory (os error 2)
    ZELLIJ_SESSION_NAME: Some("diligent-lake")
    Zellij version     : 0.40.1

Dependencies
    file             : 5.45
    ueberzugpp       : No such file or directory (os error 2)
    ffmpegthumbnailer: No such file or directory (os error 2)
    magick           : No such file or directory (os error 2)
    fzf              : 0.44.1
    fd               : No such file or directory (os error 2)
    rg               : No such file or directory (os error 2)
    chafa            : No such file or directory (os error 2)
    zoxide           : 0.9.6
    7z               : No such file or directory (os error 2)
    7zz              : No such file or directory (os error 2)
    jq               : 1.7


--------------------------------------------------
When reporting a bug, please also upload the `yazi.log` log file - only upload the most recent content by time.
You can find it in the "/home/lucca/.local/state/yazi" directory.
```


### Please describe the problem you're trying to solve

zoxide usage will be broken inside yazi if the user does not have fzf installed

### Would you be willing to contribute this feature?

- [ ] Yes, I'll give it a shot

### Describe the solution you'd like

Give users a prompt if they don't have fzf installed

This could be after running yazi for the first time or when the user tries to run zoxide for the first time

### Additional context

_No response_

### Validations

- [X] I have searched the existing issues/discussions
- [X] The [latest nightly build](https://yazi-rs.github.io/docs/installation/#official-binaries) doesn't already have this feature


Discussion / hints:

Fix patch:
diff --git a/yazi-plugin/preset/plugins/zoxide.lua b/yazi-plugin/preset/plugins/zoxide.lua
index 39ff6c94b..2b40903b5 100644
--- a/yazi-plugin/preset/plugins/zoxide.lua
+++ b/yazi-plugin/preset/plugins/zoxide.lua
@@ -9,7 +9,7 @@ local set_state = ya.sync(function(st, empty) st.empty = empty end)
 
 local function fail(s, ...) ya.notify { title = "Zoxide", content = s:format(...), timeout = 5, level = "error" } end
 
-local function opts()
+local function options()
 	-- https://github.com/ajeetdsouza/zoxide/blob/main/src/cmd/query.rs#L92
 	local default = {
 		-- Search mode
@@ -88,27 +88,27 @@ local function entry()
 	end
 
 	local _permit = ya.hide()
-	local child, err = Command("zoxide")
+	local child, err1 = Command("zoxide")
 		:args({ "query", "-i", "--exclude" })
 		:arg(st.cwd)
 		:env("SHELL", "sh")
 		:env("CLICOLOR", "1")
 		:env("CLICOLOR_FORCE", "1")
-		:env("_ZO_FZF_OPTS", opts())
+		:env("_ZO_FZF_OPTS", options())
 		:stdin(Command.INHERIT)
 		:stdout(Command.PIPED)
-		:stderr(Command.INHERIT)
+		:stderr(Command.PIPED)
 		:spawn()
 
 	if not child then
-		return fail("Failed to start `zoxide`, error: " .. err)
+		return fail("Failed to start `zoxide`, error: " .. err1)
 	end
 
-	local output, err = child:wait_with_output()
+	local output, err2 = child:wait_with_output()
 	if not output then
-		return fail("Cannot read `zoxide` output, error: " .. err)
+		return fail("Cannot read `zoxide` output, error: " .. err2)
 	elseif not output.status.success and output.status.code ~= 130 then
-		return fail("`zoxide` exited with error code %s", output.status.code)
+		return fail("`zoxide` exited with code %s: %s", output.status.code, output.stderr:gsub("^zoxide:%s*", ""))
 	end
 
 	local target = output.stdout:gsub("\n$", "")


Test patch: