From 0ada74efbe11de17b9cc3588f91eb1f465b518f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Fri, 28 Mar 2025 20:14:53 +0800 Subject: [PATCH] feat: new `follow` command to follow files pointed to by symlinks (#2543) --- yazi-boot/src/actions/debug.rs | 19 +++++++++++++++++++ yazi-config/preset/keymap-default.toml | 23 ++++++++++++----------- yazi-core/src/tab/commands/follow.rs | 24 ++++++++++++++++++++++++ yazi-core/src/tab/commands/mod.rs | 1 + yazi-fm/src/executor.rs | 1 + yazi-fs/src/path.rs | 3 +++ 6 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 yazi-core/src/tab/commands/follow.rs diff --git a/yazi-boot/src/actions/debug.rs b/yazi-boot/src/actions/debug.rs index ea7340b4..c6e4fcde 100644 --- a/yazi-boot/src/actions/debug.rs +++ b/yazi-boot/src/actions/debug.rs @@ -2,6 +2,7 @@ use std::{env, ffi::OsStr, fmt::Write}; use regex::Regex; use yazi_adapter::Mux; +use yazi_shared::timestamp_us; use super::Actions; @@ -100,6 +101,9 @@ impl Actions { writeln!(s, " xclip : {}", Self::process_output("xclip", "-version"))?; writeln!(s, " xsel : {}", Self::process_output("xsel", "--version"))?; + writeln!(s, "\nRoutine")?; + writeln!(s, " `file -bL --mime-type`: {}", Self::file1_output())?; + writeln!( s, "\n\nSee https://yazi-rs.github.io/docs/plugins/overview#debugging on how to enable logging or debug runtime errors." @@ -132,4 +136,19 @@ impl Actions { Err(e) => format!("{e}"), } } + + fn file1_output() -> String { + use std::io::Write; + + let p = env::temp_dir().join(format!("yazi-debug-{}", timestamp_us())); + std::fs::File::create_new(&p).map(|mut f| f.write_all(b"Hello, World!")).ok(); + + let cmd = env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()); + match std::process::Command::new(cmd).args(["-bL", "--mime-type"]).arg(&p).output() { + Ok(out) => { + String::from_utf8_lossy(&out.stdout).trim().lines().next().unwrap_or_default().to_owned() + } + Err(e) => format!("{e}"), + } + } } diff --git a/yazi-config/preset/keymap-default.toml b/yazi-config/preset/keymap-default.toml index e60a9266..f4cda809 100644 --- a/yazi-config/preset/keymap-default.toml +++ b/yazi-config/preset/keymap-default.toml @@ -128,6 +128,7 @@ keymap = [ { on = [ "g", "c" ], run = "cd ~/.config", desc = "Go ~/.config" }, { on = [ "g", "d" ], run = "cd ~/Downloads", desc = "Go ~/Downloads" }, { on = [ "g", "" ], run = "cd --interactive", desc = "Jump interactively" }, + { on = [ "g", "f" ], run = "follow", desc = "Follow hovered symlink" }, # Tabs { on = "t", run = "tab_create --current", desc = "Create a new tab with CWD" }, @@ -228,8 +229,8 @@ keymap = [ keymap = [ { on = "", run = "close", desc = "Cancel input" }, { on = "", run = "close --submit", desc = "Submit input" }, - { on = "", run = "escape", desc = "Go back the normal mode, or cancel input" }, - { on = "", run = "escape", desc = "Go back the normal mode, or cancel input" }, + { on = "", run = "escape", desc = "Back to normal mode, or cancel input" }, + { on = "", run = "escape", desc = "Back to normal mode, or cancel input" }, # Mode { on = "i", run = "insert", desc = "Enter insert mode" }, @@ -285,14 +286,14 @@ keymap = [ { on = "", run = "kill forward", desc = "Kill forwards to the end of the current word" }, # Cut/Yank/Paste - { on = "d", run = "delete --cut", desc = "Cut the selected characters" }, - { on = "D", run = [ "delete --cut", "move eol" ], desc = "Cut until the EOL" }, - { on = "c", run = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" }, - { on = "C", run = [ "delete --cut --insert", "move eol" ], desc = "Cut until the EOL, and enter insert mode" }, - { on = "x", run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" }, - { on = "y", run = "yank", desc = "Copy the selected characters" }, - { on = "p", run = "paste", desc = "Paste the copied characters after the cursor" }, - { on = "P", run = "paste --before", desc = "Paste the copied characters before the cursor" }, + { on = "d", run = "delete --cut", desc = "Cut selected characters" }, + { on = "D", run = [ "delete --cut", "move eol" ], desc = "Cut until EOL" }, + { on = "c", run = "delete --cut --insert", desc = "Cut selected characters, and enter insert mode" }, + { on = "C", run = [ "delete --cut --insert", "move eol" ], desc = "Cut until EOL, and enter insert mode" }, + { on = "x", run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut current character" }, + { on = "y", run = "yank", desc = "Copy selected characters" }, + { on = "p", run = "paste", desc = "Paste copied characters after the cursor" }, + { on = "P", run = "paste --before", desc = "Paste copied characters before the cursor" }, # Undo/Redo { on = "u", run = "undo", desc = "Undo the last operation" }, @@ -361,5 +362,5 @@ keymap = [ { on = "", run = "arrow next", desc = "Next line" }, # Filtering - { on = "f", run = "filter", desc = "Apply a filter for the help items" }, + { on = "f", run = "filter", desc = "Filter help items" }, ] diff --git a/yazi-core/src/tab/commands/follow.rs b/yazi-core/src/tab/commands/follow.rs new file mode 100644 index 00000000..8fb43361 --- /dev/null +++ b/yazi-core/src/tab/commands/follow.rs @@ -0,0 +1,24 @@ +use yazi_fs::clean_url; +use yazi_shared::event::CmdCow; + +use crate::tab::Tab; + +struct Opt; + +impl From for Opt { + fn from(_: CmdCow) -> Self { Self } +} + +impl Tab { + #[yazi_codegen::command] + pub fn follow(&mut self, _: Opt) { + let Some(file) = self.hovered() else { return }; + let Some(link_to) = &file.link_to else { return }; + + if link_to.is_absolute() { + self.reveal(link_to.to_owned()); + } else if let Some(p) = file.url.parent_url() { + self.reveal(clean_url(&p.join(link_to))); + } + } +} diff --git a/yazi-core/src/tab/commands/mod.rs b/yazi-core/src/tab/commands/mod.rs index 07a48ec4..08f9a65d 100644 --- a/yazi-core/src/tab/commands/mod.rs +++ b/yazi-core/src/tab/commands/mod.rs @@ -23,4 +23,5 @@ yazi_macro::mod_flat!( update_peeked update_spotted visual_mode + follow, ); diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index ea12e183..89b9fce4 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -97,6 +97,7 @@ impl<'a> Executor<'a> { on!(ACTIVE, forward); on!(ACTIVE, cd); on!(ACTIVE, reveal); + on!(ACTIVE, follow); // Toggle on!(ACTIVE, toggle); diff --git a/yazi-fs/src/path.rs b/yazi-fs/src/path.rs index 24ca438b..a7640df2 100644 --- a/yazi-fs/src/path.rs +++ b/yazi-fs/src/path.rs @@ -5,6 +5,9 @@ use yazi_shared::url::{Loc, Url}; use crate::CWD; +#[inline] +pub fn clean_url(url: &Url) -> Url { Url::from(clean_path(url)) } + #[inline] pub fn clean_path(path: impl AsRef) -> PathBuf { _clean_path(path.as_ref()) }