local M = {} local state = ya.sync(function(st) return { cwd = tostring(cx.active.current.cwd), empty = st.empty, } end) local set_state = ya.sync(function(st, empty) st.empty = empty end) function M:setup(opts) opts = opts or {} if opts.update_db then ps.sub("cd", function() local cwd = cx.active.current.cwd ya.async(function() Command("zoxide"):arg({ "add", tostring(cwd) }):status() end) end) end end function M:entry() local st = state() if st.empty == nil then st.empty = M.is_empty(st.cwd) set_state(st.empty) end if st.empty then return ya.notify { title = "Zoxide", content = "No directory history found, check Zoxide's doc to set it up and restart Yazi.", timeout = 5, level = "error", } end local permit = ui.hide() local target, err = M.run_with(st.cwd) permit:drop() if not target then ya.notify { title = "Zoxide", content = tostring(err), timeout = 5, level = "error" } elseif target ~= "" then ya.emit("cd", { target, raw = true }) end end function M.options() -- https://github.com/ajeetdsouza/zoxide/blob/main/src/cmd/query.rs#L92 local default = { -- Search mode "--exact", -- Search result "--no-sort", -- Interface "--bind=ctrl-z:ignore,btab:up,tab:down", "--cycle", "--keep-right", -- Layout "--layout=reverse", "--height=100%", "--border", "--scrollbar=▌", "--info=inline", -- Display "--tabstop=1", -- Scripting "--exit-0", } if ya.target_family() == "unix" then default[#default + 1] = "--preview-window=down,30%,sharp" if ya.target_os() == "linux" then default[#default + 1] = [[--preview='\command -p ls -Cp --color=always --group-directories-first {2..}']] else default[#default + 1] = [[--preview='\command -p ls -Cp {2..}']] end end return (os.getenv("FZF_DEFAULT_OPTS") or "") .. " " .. table.concat(default, " ") .. " " .. (os.getenv("YAZI_ZOXIDE_OPTS") or "") end ---@param cwd string ---@return boolean function M.is_empty(cwd) local child = Command("zoxide"):arg({ "query", "-l", "--exclude", cwd }):stdout(Command.PIPED):spawn() if not child then return true end local first = child:read_line() child:start_kill() return not first end ---@param cwd string ---@return string?, Error? function M.run_with(cwd) local child, err = Command("zoxide") :arg({ "query", "-i", "--exclude", cwd }) :env("SHELL", "sh") :env("CLICOLOR", 1) :env("CLICOLOR_FORCE", 1) :env("_ZO_FZF_OPTS", M.options()) :stdin(Command.INHERIT) :stdout(Command.PIPED) :stderr(Command.PIPED) :spawn() if not child then return nil, Err("Failed to start `zoxide`, error: %s", err) end local output, err = child:wait_with_output() if not output then return nil, Err("Cannot read `zoxide` output, error: %s", err) elseif not output.status.success and output.status.code ~= 130 then return nil, Err("`zoxide` exited with code %s: %s", output.status.code, output.stderr:gsub("^zoxide:%s*", "")) end return output.stdout:gsub("\n$", ""), nil end return M