From 37880d34ac079333dbf67debae48589d3835a93a Mon Sep 17 00:00:00 2001 From: Linus789 <23507341+Linus789@users.noreply.github.com> Date: Wed, 13 Sep 2023 20:40:02 +0200 Subject: [PATCH] feat: multiple openers for a single rule --- config/docs/yazi.md | 2 ++ config/src/open/open.rs | 70 ++++++++++++++++++++++++++++++++++++----- core/src/tasks/tasks.rs | 8 +++-- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/config/docs/yazi.md b/config/docs/yazi.md index 7e6f42555..aa4faedf2 100644 --- a/config/docs/yazi.md +++ b/config/docs/yazi.md @@ -76,6 +76,8 @@ rules = [ # { mime = "application/json", use = "text" }, { name = "*.json", use = "text" }, + + { name = "*.html", use = [ "browser", "text" ] }, ] ``` diff --git a/config/src/open/open.rs b/config/src/open/open.rs index 8575301c5..0b7c58800 100644 --- a/config/src/open/open.rs +++ b/config/src/open/open.rs @@ -1,7 +1,7 @@ -use std::{collections::BTreeMap, path::Path}; +use std::{collections::BTreeMap, fmt, path::Path}; use indexmap::IndexSet; -use serde::{Deserialize, Deserializer}; +use serde::{de::{self, Visitor}, Deserialize, Deserializer}; use shared::MIME_DIR; use super::Opener; @@ -18,7 +18,8 @@ struct OpenRule { name: Option, mime: Option, #[serde(rename = "use")] - use_: String, + #[serde(deserialize_with = "deserialize_from_str_or_vec")] + use_: Vec, } impl Default for Open { @@ -26,7 +27,7 @@ impl Default for Open { } impl Open { - pub fn openers(&self, path: P, mime: M) -> Option<&IndexSet> + pub fn openers(&self, path: P, mime: M) -> Option> where P: AsRef, M: AsRef, @@ -36,7 +37,18 @@ impl Open { if rule.mime.as_ref().map_or(false, |m| m.matches(&mime)) || rule.name.as_ref().map_or(false, |n| n.match_path(&path, is_folder)) { - self.openers.get(&rule.use_) + let openers = rule + .use_ + .iter() + .filter_map(|use_name| self.openers.get(use_name)) + .flatten() + .collect::>(); + + if openers.is_empty() { + return None; + } + + Some(openers) } else { None } @@ -49,13 +61,13 @@ impl Open { P: AsRef, M: AsRef, { - self.openers(path, mime).and_then(|o| o.iter().find(|o| o.block)) + self.openers(path, mime).and_then(|o| o.iter().find(|o| o.block).copied()) } pub fn common_openers(&self, targets: &[(impl AsRef, impl AsRef)]) -> Vec<&Opener> { let grouped = targets.iter().filter_map(|(p, m)| self.openers(p, m)).collect::>(); - let flat = grouped.iter().flat_map(|&g| g).collect::>(); - flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).collect() + let flat = grouped.iter().flatten().collect::>(); + flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).copied().collect() } } @@ -79,3 +91,45 @@ impl<'de> Deserialize<'de> for Open { Ok(Self { openers, rules: outer.open.rules }) } } + +fn deserialize_from_str_or_vec<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + struct StringVisitor; + + impl<'de> Visitor<'de> for StringVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string, or array of strings") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut strs = Vec::new(); + while let Some(value) = seq.next_element::()? { + strs.push(value); + } + Ok(strs) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Ok(vec![value.to_owned()]) + } + + fn visit_string(self, v: String) -> Result + where + E: de::Error, + { + Ok(vec![v]) + } + } + + deserializer.deserialize_any(StringVisitor) +} diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index c75ec048a..ec7f6e477 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -133,8 +133,12 @@ impl Tasks { pub fn file_open(&self, targets: &[(impl AsRef, impl AsRef)]) -> bool { let mut openers = BTreeMap::new(); - for (path, mime) in targets { - if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first()) { + let valid_openers = targets + .iter() + .filter_map(|(path, mime)| OPEN.openers(path, mime).map(|o| (path, o))) + .collect::>(); + for (path, opener) in &valid_openers { + if let Some(opener) = opener.first() { openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str()); } }