From 6e4ab8e3a63129acdcb15fbb50b7cb529e334890 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Wed, 14 Feb 2024 16:02:31 +0800 Subject: [PATCH] feat: expose selected/yanked files as API --- Cargo.lock | 137 +++++++++++----------- yazi-config/preset/keymap.toml | 4 +- yazi-config/preset/yazi.toml | 4 +- yazi-core/src/manager/commands/link.rs | 5 +- yazi-core/src/manager/commands/paste.rs | 11 +- yazi-core/src/manager/commands/unyank.rs | 2 +- yazi-core/src/manager/commands/yank.rs | 6 +- yazi-core/src/manager/manager.rs | 6 +- yazi-core/src/manager/mod.rs | 2 + yazi-core/src/manager/yanked.rs | 15 +++ yazi-core/src/tab/commands/escape.rs | 14 ++- yazi-fm/src/help/layout.rs | 2 +- yazi-fm/src/lives/file.rs | 7 +- yazi-fm/src/lives/lives.rs | 3 + yazi-fm/src/lives/mod.rs | 4 + yazi-fm/src/lives/selected.rs | 52 ++++++++ yazi-fm/src/lives/tab.rs | 7 +- yazi-fm/src/lives/yanked.rs | 13 ++ yazi-fm/src/notify/notify.rs | 6 +- yazi-fm/src/root.rs | 2 +- yazi-plugin/preset/components/current.lua | 17 ++- yazi-plugin/preset/components/folder.lua | 8 +- yazi-plugin/preset/components/parent.lua | 21 +++- yazi-plugin/preset/components/status.lua | 8 +- yazi-plugin/preset/inspect/inspect.lua | 6 - yazi-plugin/preset/plugins/folder.lua | 24 +++- yazi-plugin/preset/ya.lua | 2 +- 27 files changed, 258 insertions(+), 130 deletions(-) create mode 100644 yazi-core/src/manager/yanked.rs create mode 100644 yazi-fm/src/lives/selected.rs create mode 100644 yazi-fm/src/lives/yanked.rs diff --git a/Cargo.lock b/Cargo.lock index b3139f0e..5da44b6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if", "once_cell", @@ -230,9 +230,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytemuck" -version = "1.14.1" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" [[package]] name = "byteorder" @@ -278,9 +278,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" dependencies = [ "clap_builder", "clap_derive", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" dependencies = [ "anstream", "anstyle", @@ -312,18 +312,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.10" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb745187d7f4d76267b37485a65e0149edd0e91a4cfcdd3f27524ad86cee9f3" +checksum = "299353be8209bd133b049bf1c63582d184a8b39fd9c04f15fe65f50f88bdfe6c" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.4.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e571d70e22ec91d34e1c5317c8308035a2280d925167646bf094fc5de1737c" +checksum = "54b3e65f91fabdd23cac3d57d39d5d938b4daabd070c335c006dccb866a61110" dependencies = [ "clap", "clap_complete", @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "clap_complete_nushell" -version = "4.4.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "948bf70d7e1f179635d3ef819ce8baa2d3074d0d57816ac37387cd6f9eed0c31" +checksum = "92cd8ac7c321605310e4b3bdc047daf27c6ca1b50f6ae59b22aa0661ab45e295" dependencies = [ "clap", "clap_complete", @@ -341,9 +341,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -353,9 +353,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clipboard-win" @@ -422,9 +422,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -547,9 +547,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encode_unicode" @@ -853,9 +853,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "home" @@ -941,9 +941,9 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown", @@ -1001,9 +1001,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -1276,9 +1276,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -1523,9 +1523,9 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "154b85ef15a5d1719bcaa193c3c81fe645cd120c156874cd660fe49fd21d1373" +checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" dependencies = [ "bitflags 2.4.2", "cassowary", @@ -1590,7 +1590,7 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] @@ -1601,15 +1601,9 @@ checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - [[package]] name = "regex-syntax" version = "0.8.2" @@ -1851,9 +1845,9 @@ checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "strum" @@ -1901,9 +1895,9 @@ dependencies = [ [[package]] name = "syntect" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02b4b303bf8d08bfeb0445cba5068a3d306b6baece1d5582171a9bf49188f91" +checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" dependencies = [ "bincode", "bitflags 1.3.2", @@ -1912,8 +1906,9 @@ dependencies = [ "once_cell", "onig", "plist", - "regex-syntax 0.7.5", + "regex-syntax", "serde", + "serde_derive", "serde_json", "thiserror", "walkdir", @@ -1921,18 +1916,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -2084,9 +2079,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "indexmap", "serde", @@ -2106,9 +2101,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a" dependencies = [ "indexmap", "serde", @@ -2188,9 +2183,9 @@ dependencies = [ [[package]] name = "trash" -version = "3.2.1" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55bb920006929bc37df8c151c3c063b6fc10f485dfe4937393f905861a632e53" +checksum = "c658458d46d9d5a153a3b5cdd88d8579ad50d4fb85d53961e4526c8fc7c55a57" dependencies = [ "chrono", "libc", @@ -2231,9 +2226,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" @@ -2352,9 +2347,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2362,9 +2357,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", @@ -2377,9 +2372,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2387,9 +2382,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", @@ -2400,9 +2395,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "weezl" @@ -2663,9 +2658,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.37" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" +checksum = "6b1dbce9e90e5404c5a52ed82b1d13fc8cfbdad85033b6f57546ffd1265f8451" dependencies = [ "memchr", ] diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 96ed0044..0d8c2fc7 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -31,8 +31,8 @@ keymap = [ { on = [ "" ], exec = "arrow -100%", desc = "Move cursor up one page" }, { on = [ "" ], exec = "arrow 100%", desc = "Move cursor down one page" }, - { on = [ "h" ], exec = [ "leave", "escape --visual --select" ], desc = "Go back to the parent directory" }, - { on = [ "l" ], exec = [ "enter", "escape --visual --select" ], desc = "Enter the child directory" }, + { on = [ "h" ], exec = [ "escape --visual", "leave" ], desc = "Go back to the parent directory" }, + { on = [ "l" ], exec = [ "escape --visual", "enter" ], desc = "Enter the child directory" }, { on = [ "H" ], exec = "back", desc = "Go back to the previous directory" }, { on = [ "L" ], exec = "forward", desc = "Go forward to the next directory" }, diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index d4eaf662..e9d46403 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -26,8 +26,8 @@ ueberzug_offset = [ 0, 0, 0, 0 ] [opener] edit = [ - { exec = '[ -n "$EDITOR" ] && $EDITOR "$@"', desc = "$EDITOR", block = true, for = "unix" }, - { exec = 'code "%*"', orphan = true, for = "windows" }, + { exec = '${EDITOR:=vi} "$@"', desc = "$EDITOR", block = true, for = "unix" }, + { exec = 'code "%*"', orphan = true, for = "windows" }, ] open = [ { exec = 'xdg-open "$@"', desc = "Open", for = "linux" }, diff --git a/yazi-core/src/manager/commands/link.rs b/yazi-core/src/manager/commands/link.rs index e6c72ce9..2329782f 100644 --- a/yazi-core/src/manager/commands/link.rs +++ b/yazi-core/src/manager/commands/link.rs @@ -15,12 +15,11 @@ impl From for Opt { impl Manager { pub fn link(&mut self, opt: impl Into, tasks: &Tasks) { - let (cut, ref src) = self.yanked; - if cut { + if self.yanked.cut { return; } let opt = opt.into() as Opt; - tasks.file_link(src, self.cwd(), opt.relative, opt.force); + tasks.file_link(&self.yanked, self.cwd(), opt.relative, opt.force); } } diff --git a/yazi-core/src/manager/commands/paste.rs b/yazi-core/src/manager/commands/paste.rs index 256ffe58..8b2c9530 100644 --- a/yazi-core/src/manager/commands/paste.rs +++ b/yazi-core/src/manager/commands/paste.rs @@ -15,14 +15,13 @@ impl From for Opt { impl Manager { pub fn paste(&mut self, opt: impl Into, tasks: &Tasks) { - let dest = self.cwd(); - let (cut, ref src) = self.yanked; - let opt = opt.into() as Opt; - if cut { - tasks.file_cut(src, dest, opt.force); + + let dest = self.cwd(); + if self.yanked.cut { + tasks.file_cut(&self.yanked, dest, opt.force); } else { - tasks.file_copy(src, dest, opt.force, opt.follow); + tasks.file_copy(&self.yanked, dest, opt.force, opt.follow); } } } diff --git a/yazi-core/src/manager/commands/unyank.rs b/yazi-core/src/manager/commands/unyank.rs index 918223f0..6071bdf9 100644 --- a/yazi-core/src/manager/commands/unyank.rs +++ b/yazi-core/src/manager/commands/unyank.rs @@ -4,7 +4,7 @@ use crate::manager::Manager; impl Manager { pub fn unyank(&mut self, _: Cmd) { - render!(!self.yanked.1.is_empty()); + render!(!self.yanked.is_empty()); self.yanked = Default::default(); } diff --git a/yazi-core/src/manager/commands/yank.rs b/yazi-core/src/manager/commands/yank.rs index 7f1209a5..79926697 100644 --- a/yazi-core/src/manager/commands/yank.rs +++ b/yazi-core/src/manager/commands/yank.rs @@ -1,6 +1,6 @@ use yazi_shared::{event::Cmd, render}; -use crate::manager::Manager; +use crate::manager::{Manager, Yanked}; pub struct Opt { cut: bool, @@ -14,8 +14,8 @@ impl Manager { pub fn yank(&mut self, opt: impl Into) { let opt = opt.into() as Opt; - self.yanked.0 = opt.cut; - self.yanked.1 = self.selected().into_iter().map(|f| f.url()).collect(); + self.yanked = + Yanked { cut: opt.cut, urls: self.selected().into_iter().map(|f| f.url()).collect() }; render!(); } } diff --git a/yazi-core/src/manager/manager.rs b/yazi-core/src/manager/manager.rs index 813593fd..7a2f9d64 100644 --- a/yazi-core/src/manager/manager.rs +++ b/yazi-core/src/manager/manager.rs @@ -1,13 +1,13 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use yazi_shared::fs::{File, Url}; -use super::{Tabs, Watcher}; +use super::{Tabs, Watcher, Yanked}; use crate::{folder::Folder, tab::Tab}; pub struct Manager { pub tabs: Tabs, - pub yanked: (bool, HashSet), + pub yanked: Yanked, pub(super) watcher: Watcher, pub mimetype: HashMap, diff --git a/yazi-core/src/manager/mod.rs b/yazi-core/src/manager/mod.rs index 8016b0e4..46c3d35d 100644 --- a/yazi-core/src/manager/mod.rs +++ b/yazi-core/src/manager/mod.rs @@ -3,8 +3,10 @@ mod linked; mod manager; mod tabs; mod watcher; +mod yanked; pub use linked::*; pub use manager::*; pub use tabs::*; pub use watcher::*; +pub use yanked::*; diff --git a/yazi-core/src/manager/yanked.rs b/yazi-core/src/manager/yanked.rs new file mode 100644 index 00000000..438fed3d --- /dev/null +++ b/yazi-core/src/manager/yanked.rs @@ -0,0 +1,15 @@ +use std::{collections::HashSet, ops::Deref}; + +use yazi_shared::fs::Url; + +#[derive(Default)] +pub struct Yanked { + pub cut: bool, + pub(super) urls: HashSet, +} + +impl Deref for Yanked { + type Target = HashSet; + + fn deref(&self) -> &Self::Target { &self.urls } +} diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index df31e2ef..fd37d2cc 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -1,7 +1,7 @@ use bitflags::bitflags; use yazi_shared::{event::Cmd, render}; -use crate::tab::{Mode, Tab}; +use crate::{manager::Manager, tab::{Mode, Tab}}; bitflags! { pub struct Opt: u8 { @@ -53,9 +53,15 @@ impl Tab { #[inline] fn escape_select(&mut self) -> bool { - let old = self.selected.len(); - self.select_all(Some(false)); - old != self.selected.len() + if self.selected.is_empty() { + return false; + } + + self.selected.clear(); + if self.current.hovered().is_some_and(|h| h.is_dir()) { + Manager::_peek(true); + } + true } #[inline] diff --git a/yazi-fm/src/help/layout.rs b/yazi-fm/src/help/layout.rs index 7326444d..1d78c169 100644 --- a/yazi-fm/src/help/layout.rs +++ b/yazi-fm/src/help/layout.rs @@ -17,7 +17,7 @@ impl<'a> Widget for Layout<'a> { let help = &self.cx.help; widgets::Clear.render(area, buf); - let chunks = layout::Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).split(area); + let chunks = layout::Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).split(area); Line::styled( help.keyword().unwrap_or_else(|| format!("{}.help", help.layer.to_string())), THEME.help.footer, diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs index 3827966a..e7eb1dc2 100644 --- a/yazi-fm/src/lives/file.rs +++ b/yazi-fm/src/lives/file.rs @@ -76,9 +76,9 @@ impl File { }); reg.add_method("is_yanked", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; - Ok(if !cx.manager.yanked.1.contains(&me.url) { + Ok(if !cx.manager.yanked.contains(&me.url) { 0u8 - } else if cx.manager.yanked.0 { + } else if cx.manager.yanked.cut { 2u8 } else { 1u8 @@ -88,8 +88,11 @@ impl File { let cx = lua.named_registry_value::("cx")?; let selected = me.tab().selected.contains(&me.url); + #[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) }) diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index d078deb4..3b45c761 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -20,9 +20,11 @@ impl Lives { super::Folder::register(&LUA)?; super::Mode::register(&LUA)?; super::Preview::register(&LUA)?; + super::Selected::register(&LUA)?; super::Tab::register(&LUA)?; super::Tabs::register(&LUA)?; super::Tasks::register(&LUA)?; + super::Yanked::register(&LUA)?; Ok(()) } @@ -42,6 +44,7 @@ impl Lives { ("active", super::Tab::make(cx.manager.active())?), ("tabs", super::Tabs::make(&cx.manager.tabs)?), ("tasks", super::Tasks::make(&cx.tasks)?), + ("yanked", scope.create_any_userdata_ref(&cx.manager.yanked)?), ])?, )?; diff --git a/yazi-fm/src/lives/mod.rs b/yazi-fm/src/lives/mod.rs index b16a4ece..52e6ff63 100644 --- a/yazi-fm/src/lives/mod.rs +++ b/yazi-fm/src/lives/mod.rs @@ -7,9 +7,11 @@ mod folder; mod lives; mod mode; mod preview; +mod selected; mod tab; mod tabs; mod tasks; +mod yanked; use config::*; use file::*; @@ -18,8 +20,10 @@ use folder::*; pub(super) use lives::*; use mode::*; use preview::*; +use selected::*; use tab::*; use tabs::*; use tasks::*; +use yanked::*; type CtxRef<'lua> = mlua::UserDataRef<'lua, crate::Ctx>; diff --git a/yazi-fm/src/lives/selected.rs b/yazi-fm/src/lives/selected.rs new file mode 100644 index 00000000..b20233fb --- /dev/null +++ b/yazi-fm/src/lives/selected.rs @@ -0,0 +1,52 @@ +use std::{collections::{btree_set, BTreeSet}, ops::Deref}; + +use mlua::{AnyUserData, Lua, MetaMethod, UserDataMethods, UserDataRefMut}; +use yazi_plugin::{bindings::Cast, url::Url}; + +use super::SCOPE; + +#[derive(Clone, Copy)] +pub(super) struct Selected { + inner: *const BTreeSet, +} + +impl Deref for Selected { + type Target = BTreeSet; + + fn deref(&self) -> &Self::Target { self.inner() } +} + +impl Selected { + #[inline] + pub(crate) fn make(inner: &BTreeSet) -> mlua::Result> { + SCOPE.create_any_userdata(Self { inner }) + } + + pub(super) fn register(lua: &Lua) -> mlua::Result<()> { + lua.register_userdata_type::(|reg| { + reg.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len())); + + reg.add_meta_method(MetaMethod::Pairs, |lua, me, ()| { + let iter = lua.create_function(|lua, mut iter: UserDataRefMut| { + Ok(if let Some(url) = iter.0.next() { Some(Url::cast(lua, url.clone())?) } else { None }) + })?; + + Ok((iter, SelectedIter::make(me.inner()))) + }); + })?; + + Ok(()) + } + + #[inline] + fn inner(&self) -> &'static BTreeSet { unsafe { &*self.inner } } +} + +struct SelectedIter(btree_set::Iter<'static, yazi_shared::fs::Url>); + +impl SelectedIter { + #[inline] + fn make(selected: &'static BTreeSet) -> mlua::Result> { + SCOPE.create_any_userdata(Self(selected.iter())) + } +} diff --git a/yazi-fm/src/lives/tab.rs b/yazi-fm/src/lives/tab.rs index f393340f..6451082d 100644 --- a/yazi-fm/src/lives/tab.rs +++ b/yazi-fm/src/lives/tab.rs @@ -2,7 +2,7 @@ use std::ops::Deref; use mlua::{AnyUserData, Lua, UserDataFields, UserDataMethods}; -use super::{Config, Folder, Mode, Preview, SCOPE}; +use super::{Config, Folder, Mode, Preview, Selected, SCOPE}; pub(super) struct Tab { inner: *const yazi_core::tab::Tab, @@ -36,10 +36,13 @@ impl Tab { reg.add_field_method_get("mode", |_, me| Mode::make(&me.mode)); reg.add_field_method_get("conf", |_, me| Config::make(&me.conf)); + reg.add_field_method_get("current", |_, me| Folder::make(None, &me.current, me)); reg.add_field_method_get("parent", |_, me| { me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose() }); - reg.add_field_method_get("current", |_, me| Folder::make(None, &me.current, me)); + + reg.add_field_method_get("selected", |_, me| Selected::make(&me.selected)); + reg.add_field_method_get("preview", |_, me| Preview::make(me)); })?; diff --git a/yazi-fm/src/lives/yanked.rs b/yazi-fm/src/lives/yanked.rs new file mode 100644 index 00000000..3051ecae --- /dev/null +++ b/yazi-fm/src/lives/yanked.rs @@ -0,0 +1,13 @@ +use mlua::{Lua, MetaMethod, UserDataFields, UserDataMethods}; + +pub(super) struct Yanked; + +impl Yanked { + pub(super) fn register(lua: &Lua) -> mlua::Result<()> { + lua.register_userdata_type::(|reg| { + reg.add_field_method_get("is_cut", |_, me| Ok(me.cut)); + + reg.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len())); + }) + } +} diff --git a/yazi-fm/src/notify/notify.rs b/yazi-fm/src/notify/notify.rs index 3a097f49..97e040f5 100644 --- a/yazi-fm/src/notify/notify.rs +++ b/yazi-fm/src/notify/notify.rs @@ -13,7 +13,11 @@ impl<'a> Notify<'a> { pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } fn chunks(area: Rect, messages: &[Message]) -> Rc<[Rect]> { - let chunks = Layout::horizontal([Constraint::Percentage(100), Constraint::Min(40)]).split(area); + let chunks = + Layout::horizontal([Constraint::Fill(1), Constraint::Length(40), Constraint::Max(1)]) + .split(area); + + let chunks = Layout::vertical([Constraint::Max(1), Constraint::Min(1)]).split(chunks[1]); Layout::vertical(messages.iter().map(|m| Constraint::Length(m.height() as u16))) .spacing(1) diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index 057e0b3b..4b75c623 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -18,7 +18,7 @@ impl<'a> Root<'a> { impl<'a> Widget for Root<'a> { fn render(self, area: Rect, buf: &mut Buffer) { let chunks = - Layout::vertical([Constraint::Length(1), Constraint::Min(0), Constraint::Length(1)]) + Layout::vertical([Constraint::Length(1), Constraint::Fill(1), Constraint::Length(1)]) .split(area); components::Header.render(chunks[0], buf); diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index 24fa14d4..bac9858d 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -5,9 +5,13 @@ Current = { function Current:render(area) self.area = area - local markers = {} - local items = {} - for i, f in ipairs(Folder:by_kind(Folder.CURRENT).window) do + local files = Folder:by_kind(Folder.CURRENT).window + if #files == 0 then + return {} + end + + local items, markers = {}, {} + for i, f in ipairs(files) do local name = Folder:highlighted_name(f) -- Highlight hovered file @@ -27,5 +31,10 @@ function Current:render(area) markers[#markers + 1] = { i, 3 } end end - return ya.flat { ui.List(area, items), Folder:linemode(area), Folder:markers(area, markers) } + + return ya.flat { + ui.List(area, items), + Folder:linemode(area, files), + Folder:markers(area, markers), + } end diff --git a/yazi-plugin/preset/components/folder.lua b/yazi-plugin/preset/components/folder.lua index 129f4674..cea95c5a 100644 --- a/yazi-plugin/preset/components/folder.lua +++ b/yazi-plugin/preset/components/folder.lua @@ -20,7 +20,7 @@ function Folder:icon(file) end function Folder:highlight_ranges(s, ranges) - if ranges == nil or #ranges == 0 then + if not ranges or #ranges == 0 then return { ui.Span(s) } end @@ -55,7 +55,7 @@ function Folder:highlighted_name(file) spans[#spans + 1] = ui.Span(" -> " .. tostring(file.link_to)):italic() end - if highlights == nil or not file:is_hovered() then + if not highlights or not file:is_hovered() then return spans end @@ -67,14 +67,14 @@ function Folder:highlighted_name(file) return spans end -function Folder:linemode(area) +function Folder:linemode(area, files) local mode = cx.active.conf.linemode if mode == "none" then return {} end local lines = {} - for _, f in ipairs(self:by_kind(self.CURRENT).window) do + for _, f in ipairs(files) do local spans = { ui.Span(" ") } if mode == "size" then local size = f:size() diff --git a/yazi-plugin/preset/components/parent.lua b/yazi-plugin/preset/components/parent.lua index 6c29bd81..c40d418b 100644 --- a/yazi-plugin/preset/components/parent.lua +++ b/yazi-plugin/preset/components/parent.lua @@ -6,21 +6,32 @@ function Parent:render(area) self.area = area local folder = Folder:by_kind(Folder.PARENT) - if folder == nil then + if not folder then return {} end - local items = {} - for _, f in ipairs(folder.window) do + local items, markers = {}, {} + for i, f in ipairs(folder.window) do + -- Highlight hovered file local item = ui.ListItem(ui.Line { Folder:icon(f), ui.Span(f.name) }) if f:is_hovered() then item = item:style(THEME.manager.hovered) else item = item:style(f:style()) 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 } + end end - return { ui.List(area, items) } + return ya.flat { + ui.List(area, items), + Folder:markers(area, markers), + } end diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index 3fc9d5ca..1e9291e1 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -27,7 +27,7 @@ end function Status:size() local h = cx.active.current.hovered - if h == nil then + if not h then return ui.Line {} end @@ -40,7 +40,7 @@ end function Status:name() local h = cx.active.current.hovered - if h == nil then + if not h then return ui.Span("") end @@ -49,12 +49,12 @@ end function Status:permissions() local h = cx.active.current.hovered - if h == nil then + if not h then return ui.Line {} end local perm = h.cha:permissions() - if perm == nil then + if not perm then return ui.Line {} end diff --git a/yazi-plugin/preset/inspect/inspect.lua b/yazi-plugin/preset/inspect/inspect.lua index be86016e..c23bee92 100644 --- a/yazi-plugin/preset/inspect/inspect.lua +++ b/yazi-plugin/preset/inspect/inspect.lua @@ -304,9 +304,3 @@ setmetatable(inspect, { ya = ya or {} ya.inspect = inspect -ya.print = function(...) - local args = { ... } - for i = 1, #args do - print(inspect(args[i])) - end -end diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index 6ebf14aa..80d82ae5 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -2,7 +2,7 @@ local M = {} function M:peek() local folder = Folder:by_kind(Folder.PREVIEW) - if folder == nil or folder.cwd ~= self.file.url then + if not folder or folder.cwd ~= self.file.url then return {} end @@ -11,8 +11,9 @@ function M:peek() ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" }) end - local items = {} - for _, f in ipairs(folder.window) do + local items, markers = {}, {} + for i, f in ipairs(folder.window) do + -- Highlight hovered file local item = ui.ListItem(ui.Line { Folder:icon(f), ui.Span(f.name) }) if f:is_hovered() then item = item:style(THEME.manager.preview_hovered) @@ -20,8 +21,23 @@ function M:peek() item = item:style(f:style()) 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 } + end end - ya.preview_widgets(self, { ui.List(self.area, items) }) + + ya.preview_widgets( + self, + ya.flat { + ui.List(self.area, items), + Folder:markers(self.area, markers), + } + ) end function M:seek(units) diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 121a25f8..7cf6fa69 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -52,7 +52,7 @@ end function ya.readable_path(path) local home = os.getenv("HOME") or os.getenv("USERPROFILE") - if home == nil then + if not home then return path elseif string.sub(path, 1, #home) == home then return "~" .. string.sub(path, #home + 1)