Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multiple openers for a single rule #154

Merged
merged 3 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/docs/yazi.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ rules = [

# { mime = "application/json", use = "text" },
{ name = "*.json", use = "text" },

# Multiple openers for a single rule
{ name = "*.html", use = [ "browser", "text" ] },
]
```

Expand Down
2 changes: 2 additions & 0 deletions config/src/open/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod open;
mod opener;
mod rule;

pub use open::*;
pub use opener::*;
use rule::*;
27 changes: 13 additions & 14 deletions config/src/open/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,20 @@ use serde::{Deserialize, Deserializer};
use shared::MIME_DIR;

use super::Opener;
use crate::{Pattern, MERGED_YAZI};
use crate::{open::OpenRule, MERGED_YAZI};

#[derive(Debug)]
pub struct Open {
openers: BTreeMap<String, IndexSet<Opener>>,
rules: Vec<OpenRule>,
}

#[derive(Debug, Deserialize)]
struct OpenRule {
name: Option<Pattern>,
mime: Option<Pattern>,
#[serde(rename = "use")]
use_: String,
}

impl Default for Open {
fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() }
}

impl Open {
pub fn openers<P, M>(&self, path: P, mime: M) -> Option<&IndexSet<Opener>>
pub fn openers<P, M>(&self, path: P, mime: M) -> Option<IndexSet<&Opener>>
where
P: AsRef<Path>,
M: AsRef<str>,
Expand All @@ -36,7 +28,14 @@ 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_| self.openers.get(use_))
.flatten()
.collect::<IndexSet<_>>();

if openers.is_empty() { None } else { Some(openers) }
} else {
None
}
Expand All @@ -49,12 +48,12 @@ impl Open {
P: AsRef<Path>,
M: AsRef<str>,
{
self.openers(path, mime).and_then(|o| o.iter().find(|o| o.block))
self.openers(path, mime).and_then(|o| o.into_iter().find(|o| o.block))
}

pub fn common_openers(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> Vec<&Opener> {
let grouped = targets.iter().filter_map(|(p, m)| self.openers(p, m)).collect::<Vec<_>>();
let flat = grouped.iter().flat_map(|&g| g).collect::<IndexSet<_>>();
let grouped: Vec<_> = targets.iter().filter_map(|(p, m)| self.openers(p, m)).collect();
let flat: IndexSet<_> = grouped.iter().flatten().copied().collect();
flat.into_iter().filter(|&o| grouped.iter().all(|g| g.contains(o))).collect()
}
}
Expand Down
58 changes: 58 additions & 0 deletions config/src/open/rule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use std::fmt;

use serde::{de::{self, Visitor}, Deserialize, Deserializer};

use crate::pattern::Pattern;

#[derive(Debug, Deserialize)]
pub(super) struct OpenRule {
pub(super) name: Option<Pattern>,
pub(super) mime: Option<Pattern>,
#[serde(rename = "use")]
#[serde(deserialize_with = "OpenRule::deserialize")]
pub(super) use_: Vec<String>,
}

impl OpenRule {
fn deserialize<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
struct UseVisitor;

impl<'de> Visitor<'de> for UseVisitor {
type Value = Vec<String>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string, or array of strings")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut uses = Vec::new();
while let Some(use_) = seq.next_element::<String>()? {
uses.push(use_);
}
Ok(uses)
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![value.to_owned()])
}

fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![v])
}
}

deserializer.deserialize_any(UseVisitor)
}
}
2 changes: 1 addition & 1 deletion core/src/tasks/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl Tasks {
pub fn file_open(&self, targets: &[(impl AsRef<Path>, impl AsRef<str>)]) -> bool {
let mut openers = BTreeMap::new();
for (path, mime) in targets {
if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first()) {
if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first().copied()) {
openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str());
}
}
Expand Down