diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 0d8c2fc79..5ae3867d5 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -58,29 +58,29 @@ keymap = [ { on = [ "" ], exec = "select_all --state=none", desc = "Inverse selection of all files" }, # Operation - { on = [ "o" ], exec = "open", desc = "Open the selected files" }, - { on = [ "O" ], exec = "open --interactive", desc = "Open the selected files interactively" }, - { on = [ "" ], exec = "open", desc = "Open the selected files" }, - { on = [ "" ], exec = "open --interactive", desc = "Open the selected files interactively" }, - { on = [ "y" ], exec = [ "yank", "escape --visual --select" ], desc = "Copy the selected files" }, - { on = [ "Y" ], exec = [ "unyank", "escape --visual --select" ], desc = "Cancel the yank status of files" }, - { on = [ "x" ], exec = [ "yank --cut", "escape --visual --select" ], desc = "Cut the selected files" }, - { on = [ "p" ], exec = "paste", desc = "Paste the files" }, - { on = [ "P" ], exec = "paste --force", desc = "Paste the files (overwrite if the destination exists)" }, - { on = [ "-" ], exec = "link", desc = "Symlink the absolute path of files" }, - { on = [ "_" ], exec = "link --relative", desc = "Symlink the relative path of files" }, - { on = [ "d" ], exec = [ "remove", "escape --visual --select" ], desc = "Move the files to the trash" }, - { on = [ "D" ], exec = [ "remove --permanently", "escape --visual --select" ], desc = "Permanently delete the files" }, - { on = [ "a" ], exec = "create", desc = "Create a file or directory (ends with / for directories)" }, - { on = [ "r" ], exec = "rename --cursor=before_ext", desc = "Rename a file or directory" }, - { on = [ ";" ], exec = "shell", desc = "Run a shell command" }, - { on = [ ":" ], exec = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" }, - { on = [ "." ], exec = "hidden toggle", desc = "Toggle the visibility of hidden files" }, - { on = [ "s" ], exec = "search fd", desc = "Search files by name using fd" }, - { on = [ "S" ], exec = "search rg", desc = "Search files by content using ripgrep" }, - { on = [ "" ], exec = "search none", desc = "Cancel the ongoing search" }, - { on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" }, - { on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" }, + { on = [ "o" ], exec = [ "escape --visual", "open" ], desc = "Open the selected files" }, + { on = [ "O" ], exec = [ "escape --visual", "open --interactive" ], desc = "Open the selected files interactively" }, + { on = [ "" ], exec = [ "escape --visual", "open" ], desc = "Open the selected files" }, + { on = [ "" ], exec = [ "escape --visual", "open --interactive" ], desc = "Open the selected files interactively" }, + { on = [ "y" ], exec = [ "escape --visual", "yank" ], desc = "Copy the selected files" }, + { on = [ "Y" ], exec = "unyank", desc = "Cancel the yank status of files" }, + { on = [ "x" ], exec = [ "escape --visual", "yank --cut" ], desc = "Cut the selected files" }, + { on = [ "p" ], exec = "paste", desc = "Paste the files" }, + { on = [ "P" ], exec = "paste --force", desc = "Paste the files (overwrite if the destination exists)" }, + { on = [ "-" ], exec = "link", desc = "Symlink the absolute path of files" }, + { on = [ "_" ], exec = "link --relative", desc = "Symlink the relative path of files" }, + { on = [ "d" ], exec = [ "escape --visual", "remove" ], desc = "Move the files to the trash" }, + { on = [ "D" ], exec = [ "escape --visual", "remove --permanently" ], desc = "Permanently delete the files" }, + { on = [ "a" ], exec = "create", desc = "Create a file or directory (ends with / for directories)" }, + { on = [ "r" ], exec = [ "escape --visual", "rename --cursor=before_ext" ], desc = "Rename a file or directory" }, + { on = [ ";" ], exec = [ "escape --visual", "shell" ], desc = "Run a shell command" }, + { on = [ ":" ], exec = [ "escape --visual", "shell --block" ], desc = "Run a shell command (block the UI until the command finishes)" }, + { on = [ "." ], exec = "hidden toggle", desc = "Toggle the visibility of hidden files" }, + { on = [ "s" ], exec = "search fd", desc = "Search files by name using fd" }, + { on = [ "S" ], exec = "search rg", desc = "Search files by content using ripgrep" }, + { on = [ "" ], exec = "search none", desc = "Cancel the ongoing search" }, + { on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" }, + { on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" }, # Linemode { on = [ "m", "s" ], exec = "linemode size", desc = "Set linemode to size" }, @@ -89,10 +89,10 @@ keymap = [ { on = [ "m", "n" ], exec = "linemode none", desc = "Set linemode to none" }, # Copy - { on = [ "c", "c" ], exec = "copy path", desc = "Copy the absolute path" }, - { on = [ "c", "d" ], exec = "copy dirname", desc = "Copy the path of the parent directory" }, - { on = [ "c", "f" ], exec = "copy filename", desc = "Copy the name of the file" }, - { on = [ "c", "n" ], exec = "copy name_without_ext", desc = "Copy the name of the file without the extension" }, + { on = [ "c", "c" ], exec = [ "escape --visual", "copy path" ], desc = "Copy the absolute path" }, + { on = [ "c", "d" ], exec = [ "escape --visual", "copy dirname" ], desc = "Copy the path of the parent directory" }, + { on = [ "c", "f" ], exec = [ "escape --visual", "copy filename" ], desc = "Copy the name of the file" }, + { on = [ "c", "n" ], exec = [ "escape --visual", "copy name_without_ext" ], desc = "Copy the name of the file without the extension" }, # Filter { on = [ "f" ], exec = "filter --smart", desc = "Filter the files" }, diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 61fb0cc6c..76231e03b 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -18,9 +18,10 @@ find_keyword = { fg = "yellow", italic = true } find_position = { fg = "magenta", bg = "reset", italic = true } # Marker -marker_selected = { fg = "lightgreen", bg = "lightgreen" } -marker_copied = { fg = "lightyellow", bg = "lightyellow" } +marker_copied = { fg = "lightgreen", bg = "lightgreen" } marker_cut = { fg = "lightred", bg = "lightred" } +marker_marked = { fg = "lightyellow", bg = "lightyellow" } +marker_selected = { fg = "lightblue", bg = "lightblue" } # Tab tab_active = { fg = "black", bg = "lightblue" } diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 089f02f3b..4deeaa992 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -20,9 +20,10 @@ pub struct Manager { find_position: Style, // Marker - marker_selected: Style, marker_copied: Style, marker_cut: Style, + marker_marked: Style, + marker_selected: Style, // Tab tab_active: Style, diff --git a/yazi-core/src/manager/commands/yank.rs b/yazi-core/src/manager/commands/yank.rs index 5886d5152..ab5dee174 100644 --- a/yazi-core/src/manager/commands/yank.rs +++ b/yazi-core/src/manager/commands/yank.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use yazi_shared::{event::Cmd, render}; use crate::manager::{Manager, Yanked}; @@ -12,10 +14,13 @@ impl From for Opt { impl Manager { pub fn yank(&mut self, opt: impl Into) { - let opt = opt.into() as Opt; + let selected: HashSet<_> = self.selected_or_hovered().into_iter().cloned().collect(); + if selected.is_empty() { + return; + } - self.yanked = - Yanked { cut: opt.cut, urls: self.selected_or_hovered().into_iter().cloned().collect() }; + self.yanked = Yanked { cut: opt.into().cut, urls: selected }; + self.active_mut().escape_select(); render!(); } } diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index fd37d2ccc..d30b0b26a 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -29,10 +29,10 @@ impl From for Opt { impl Tab { #[inline] - fn escape_find(&mut self) -> bool { self.finder.take().is_some() } + pub fn escape_find(&mut self) -> bool { self.finder.take().is_some() } #[inline] - fn escape_visual(&mut self) -> bool { + pub fn escape_visual(&mut self) -> bool { let Some((_, indices)) = self.mode.visual() else { return false; }; @@ -52,7 +52,7 @@ impl Tab { } #[inline] - fn escape_select(&mut self) -> bool { + pub fn escape_select(&mut self) -> bool { if self.selected.is_empty() { return false; } @@ -65,14 +65,14 @@ impl Tab { } #[inline] - fn escape_filter(&mut self) -> bool { + pub fn escape_filter(&mut self) -> bool { let b = self.current.files.filter().is_some(); self.filter_do(super::filter::Opt::default()); b } #[inline] - fn escape_search(&mut self) -> bool { + pub fn escape_search(&mut self) -> bool { let b = self.current.cwd.is_search(); self.search_stop(); b diff --git a/yazi-core/src/tab/mode.rs b/yazi-core/src/tab/mode.rs index 1a80ab2bf..af8828938 100644 --- a/yazi-core/src/tab/mode.rs +++ b/yazi-core/src/tab/mode.rs @@ -26,15 +26,6 @@ impl Mode { Mode::Unset(start, indices) => Some((*start, indices)), } } - - #[inline] - pub fn pending(&self, idx: usize, state: bool) -> bool { - match self { - Mode::Normal => state, - Mode::Select(_, indices) => state || indices.contains(&idx), - Mode::Unset(_, indices) => state && !indices.contains(&idx), - } - } } impl Mode { diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs index e7eb1dc29..99e6249f8 100644 --- a/yazi-fm/src/lives/file.rs +++ b/yazi-fm/src/lives/file.rs @@ -84,19 +84,19 @@ impl File { 1u8 }) }); - reg.add_method("is_selected", |lua, me, ()| { - let cx = lua.named_registry_value::("cx")?; - let selected = me.tab().selected.contains(&me.url); + reg.add_method("is_marked", |_, me, ()| { + use yazi_core::tab::Mode::*; + if !me.tab().mode.is_visual() || me.folder().cwd != me.tab().current.cwd { + return Ok(0u8); + } - #[allow(clippy::if_same_then_else)] - Ok(if !cx.manager.active().mode.is_visual() { - selected - } else if me.folder().cwd != me.tab().current.cwd { - selected - } else { - cx.manager.active().mode.pending(me.idx, selected) + Ok(match &me.tab().mode { + Select(_, indices) if indices.contains(&me.idx) => 1u8, + Unset(_, indices) if indices.contains(&me.idx) => 2u8, + _ => 0u8, }) }); + reg.add_method("is_selected", |_, me, ()| Ok(me.tab().selected.contains(&me.url))); reg.add_method("found", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; let Some(finder) = &cx.manager.active().finder else { diff --git a/yazi-fm/src/lives/mode.rs b/yazi-fm/src/lives/mode.rs index f5fd0add8..46f0742d3 100644 --- a/yazi-fm/src/lives/mode.rs +++ b/yazi-fm/src/lives/mode.rs @@ -25,7 +25,6 @@ impl Mode { reg.add_field_method_get("is_select", |_, me| Ok(me.is_select())); reg.add_field_method_get("is_unset", |_, me| Ok(me.is_unset())); reg.add_field_method_get("is_visual", |_, me| Ok(me.is_visual())); - reg.add_method("pending", |_, me, (idx, state): (usize, bool)| Ok(me.pending(idx, state))); reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string())); }) diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index bac9858dc..6c457df7e 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -23,12 +23,10 @@ function Current:render(area) end items[#items + 1] = item - -- Mark yanked/selected files - local yanked = f:is_yanked() - if yanked ~= 0 then - markers[#markers + 1] = { i, yanked } - elseif f:is_selected() then - markers[#markers + 1] = { i, 3 } + -- Yanked/marked/selected files + local marker = Folder:marker(f) + if marker ~= 0 then + markers[#markers + 1] = { i, marker } end end diff --git a/yazi-plugin/preset/components/folder.lua b/yazi-plugin/preset/components/folder.lua index cea95c5af..b4c0ef344 100644 --- a/yazi-plugin/preset/components/folder.lua +++ b/yazi-plugin/preset/components/folder.lua @@ -92,6 +92,21 @@ function Folder:linemode(area, files) return ui.Paragraph(area, lines):align(ui.Paragraph.RIGHT) end +function Folder:marker(file) + local yanked = file:is_yanked() + if yanked ~= 0 then + return yanked -- 1: copied, 2: cut + end + + local marked = file:is_marked() + if marked == 1 then + return 3 -- 3: marked + elseif marked == 0 and file:is_selected() then + return 4 -- 4: selected + end + return 0 +end + function Folder:markers(area, markers) if #markers == 0 or area.w * area.h == 0 then return {} @@ -115,6 +130,8 @@ function Folder:markers(area, markers) elseif last[3] == 2 then bar = bar:style(THEME.manager.marker_cut) elseif last[3] == 3 then + bar = bar:style(THEME.manager.marker_marked) + elseif last[3] == 4 then bar = bar:style(THEME.manager.marker_selected) end elements[#elements + 1] = bar diff --git a/yazi-plugin/preset/components/parent.lua b/yazi-plugin/preset/components/parent.lua index c40d418b9..be5d5e5ed 100644 --- a/yazi-plugin/preset/components/parent.lua +++ b/yazi-plugin/preset/components/parent.lua @@ -21,12 +21,10 @@ function Parent:render(area) end items[#items + 1] = item - -- Mark yanked/selected files - local yanked = f:is_yanked() - if yanked ~= 0 then - markers[#markers + 1] = { i, yanked } - elseif f:is_selected() then - markers[#markers + 1] = { i, 3 } + -- Yanked/marked/selected files + local marker = Folder:marker(f) + if marker ~= 0 then + markers[#markers + 1] = { i, marker } end end diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index 80d82ae57..a54602052 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -22,12 +22,10 @@ function M:peek() end items[#items + 1] = item - -- Mark yanked/selected files - local yanked = f:is_yanked() - if yanked ~= 0 then - markers[#markers + 1] = { i, yanked } - elseif f:is_selected() then - markers[#markers + 1] = { i, 3 } + -- Yanked/marked/selected files + local marker = Folder:marker(f) + if marker ~= 0 then + markers[#markers + 1] = { i, marker } end end