Skip to content

Commit

Permalink
feat: spotter
Browse files Browse the repository at this point in the history
  • Loading branch information
sxyazi committed Nov 15, 2024
1 parent c65bdb3 commit 5f58c6e
Show file tree
Hide file tree
Showing 76 changed files with 839 additions and 240 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ parking_lot = "0.12.3"
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] }
regex = "1.11.1"
scopeguard = "1.2.0"
serde = { version = "1.0.214", features = [ "derive" ] }
serde = { version = "1.0.215", features = [ "derive" ] }
serde_json = "1.0.132"
shell-words = "1.1.0"
tokio = { version = "1.41.1", features = [ "full" ] }
Expand Down
32 changes: 28 additions & 4 deletions yazi-config/preset/keymap.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ keymap = [
{ on = "H", run = "back", desc = "Go back to the previous directory" },
{ on = "L", run = "forward", desc = "Go forward to the next directory" },

# Seeking
{ on = "K", run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = "J", run = "seek 5", desc = "Seek down 5 units in the preview" },

# Toggle
{ on = "<Space>", run = [ "toggle", "arrow 1" ], desc = "Toggle the current selection state" },
{ on = "<C-a>", run = "toggle_all on", desc = "Select all files" },
Expand All @@ -55,6 +51,13 @@ keymap = [
{ on = "v", run = "visual_mode", desc = "Enter visual mode (selection mode)" },
{ on = "V", run = "visual_mode --unset", desc = "Enter visual mode (unset mode)" },

# Seeking
{ on = "K", run = "seek -5", desc = "Seek up 5 units in the preview" },
{ on = "J", run = "seek 5", desc = "Seek down 5 units in the preview" },

# Spotting
{ on = "<Tab>", run = "spot", desc = "Spot hovered file" },

# Operation
{ on = "o", run = "open", desc = "Open selected files" },
{ on = "O", run = "open --interactive", desc = "Open selected files interactively" },
Expand Down Expand Up @@ -175,6 +178,27 @@ keymap = [
{ on = "<F1>", run = "help", desc = "Open help" },
]

[spot]

keymap = [
{ on = "<Esc>", run = "close", desc = "Close the spot" },
{ on = "<C-[>", run = "close", desc = "Close the spot" },
{ on = "<C-c>", run = "close", desc = "Close the spot" },
{ on = "<Tab>", run = "close", desc = "Close the spot" },

{ on = "k", run = "arrow -1", desc = "Move cursor up" },
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
{ on = "h", run = "swipe -1", desc = "Swipe to the next file" },
{ on = "l", run = "swipe 1", desc = "Swipe to the previous file" },

{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },

# Help
{ on = "~", run = "help", desc = "Open help" },
{ on = "<F1>", run = "help", desc = "Open help" },
]

[pick]

keymap = [
Expand Down
13 changes: 13 additions & 0 deletions yazi-config/preset/yazi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ fetchers = [
# Mimetype
{ id = "mime", name = "*", run = "mime", if = "!mime", prio = "high" },
]
spotters = [
{ name = "*/", run = "folder" },
# Code
{ mime = "text/*", run = "code" },
{ mime = "*/{xml,javascript,x-wine-extension-ini}", run = "code" },
# Image
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
{ mime = "image/*", run = "image" },
# Video
{ mime = "video/*", run = "video" },
# Fallback
{ name = "*", run = "file" },
]
preloaders = [
# Image
{ mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" },
Expand Down
7 changes: 6 additions & 1 deletion yazi-config/src/keymap/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::Preset;
pub struct Keymap {
pub manager: Vec<Chord>,
pub tasks: Vec<Chord>,
pub spot: Vec<Chord>,
pub pick: Vec<Chord>,
pub input: Vec<Chord>,
pub confirm: Vec<Chord>,
Expand All @@ -26,6 +27,7 @@ impl Keymap {
Layer::App => unreachable!(),
Layer::Manager => &self.manager,
Layer::Tasks => &self.tasks,
Layer::Spot => &self.spot,
Layer::Pick => &self.pick,
Layer::Input => &self.input,
Layer::Confirm => &self.confirm,
Expand Down Expand Up @@ -53,6 +55,7 @@ impl<'de> Deserialize<'de> for Keymap {
struct Shadow {
manager: Inner,
tasks: Inner,
spot: Inner,
pick: Inner,
input: Inner,
confirm: Inner,
Expand Down Expand Up @@ -90,7 +93,9 @@ impl<'de> Deserialize<'de> for Keymap {
#[rustfmt::skip]
tasks: mix(shadow.tasks.prepend_keymap, shadow.tasks.keymap, shadow.tasks.append_keymap),
#[rustfmt::skip]
pick: mix(shadow.pick.prepend_keymap, shadow.pick.keymap, shadow.pick.append_keymap),
spot: mix(shadow.spot.prepend_keymap, shadow.spot.keymap, shadow.spot.append_keymap),
#[rustfmt::skip]
pick: mix(shadow.pick.prepend_keymap, shadow.pick.keymap, shadow.pick.append_keymap),
#[rustfmt::skip]
input: mix(shadow.input.prepend_keymap, shadow.input.keymap, shadow.input.append_keymap),
#[rustfmt::skip]
Expand Down
8 changes: 4 additions & 4 deletions yazi-config/src/plugin/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ impl Fetcher {
#[derive(Debug, Clone)]
pub struct FetcherProps {
pub id: u8,
pub name: String,
pub name: &'static str,
pub prio: Priority,
}

impl From<&Fetcher> for FetcherProps {
fn from(fetcher: &Fetcher) -> Self {
Self { id: fetcher.idx, name: fetcher.run.name.to_owned(), prio: fetcher.prio }
impl From<&'static Fetcher> for FetcherProps {
fn from(fetcher: &'static Fetcher) -> Self {
Self { id: fetcher.idx, name: &fetcher.run.name, prio: fetcher.prio }
}
}
2 changes: 1 addition & 1 deletion yazi-config/src/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
yazi_macro::mod_flat!(fetcher plugin preloader previewer);
yazi_macro::mod_flat!(fetcher plugin preloader previewer spotter);

pub const MAX_PREWORKERS: u8 = 32;
36 changes: 28 additions & 8 deletions yazi-config/src/plugin/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ use std::{collections::HashSet, path::Path, str::FromStr};
use anyhow::Context;
use serde::{Deserialize, Deserializer};

use super::{Fetcher, Preloader, Previewer};
use super::{Fetcher, Preloader, Previewer, Spotter};
use crate::{Preset, plugin::MAX_PREWORKERS};

pub struct Plugin {
pub fetchers: Vec<Fetcher>,
pub spotters: Vec<Spotter>,
pub preloaders: Vec<Preloader>,
pub previewers: Vec<Previewer>,
}

impl Plugin {
pub fn fetchers<'a>(
&'a self,
pub fn fetchers<'a, 'b: 'a>(
&'b self,
path: &'a Path,
mime: &'a str,
factor: impl Fn(&str) -> bool + Copy,
) -> impl Iterator<Item = &'a Fetcher> {
factor: impl Fn(&str) -> bool + Copy + 'a,
) -> impl Iterator<Item = &'b Fetcher> + 'a {
let mut seen = HashSet::new();
self.fetchers.iter().filter(move |&f| {
if seen.contains(&f.id) || !f.matches(path, mime, factor) {
Expand All @@ -34,11 +35,15 @@ impl Plugin {
self.fetchers.iter().fold(0, |n, f| if f.mime.is_some() { n } else { n | 1 << f.idx as u32 })
}

pub fn preloaders<'a>(
&'a self,
pub fn spotter(&self, path: &Path, mime: &str) -> Option<&Spotter> {
self.spotters.iter().find(|&p| p.matches(path, mime))
}

pub fn preloaders<'a, 'b: 'a>(
&'b self,
path: &'a Path,
mime: &'a str,
) -> impl Iterator<Item = &'a Preloader> {
) -> impl Iterator<Item = &'b Preloader> + 'a {
let mut next = true;
self.preloaders.iter().filter(move |&p| {
if !next || !p.matches(path, mime) {
Expand Down Expand Up @@ -80,6 +85,12 @@ impl<'de> Deserialize<'de> for Plugin {
#[serde(default)]
append_fetchers: Vec<Fetcher>,

spotters: Vec<Spotter>,
#[serde(default)]
prepend_spotters: Vec<Spotter>,
#[serde(default)]
append_spotters: Vec<Spotter>,

preloaders: Vec<Preloader>,
#[serde(default)]
prepend_preloaders: Vec<Preloader>,
Expand All @@ -94,6 +105,12 @@ impl<'de> Deserialize<'de> for Plugin {
}

let mut shadow = Outer::deserialize(deserializer)?.plugin;
if shadow.append_spotters.iter().any(|r| r.any_file()) {
shadow.spotters.retain(|r| !r.any_file());
}
if shadow.append_spotters.iter().any(|r| r.any_dir()) {
shadow.spotters.retain(|r| !r.any_dir());
}
if shadow.append_previewers.iter().any(|r| r.any_file()) {
shadow.previewers.retain(|r| !r.any_file());
}
Expand All @@ -103,6 +120,8 @@ impl<'de> Deserialize<'de> for Plugin {

shadow.fetchers =
Preset::mix(shadow.prepend_fetchers, shadow.fetchers, shadow.append_fetchers).collect();
shadow.spotters =
Preset::mix(shadow.prepend_spotters, shadow.spotters, shadow.append_spotters).collect();
shadow.preloaders =
Preset::mix(shadow.prepend_preloaders, shadow.preloaders, shadow.append_preloaders).collect();
shadow.previewers =
Expand All @@ -121,6 +140,7 @@ impl<'de> Deserialize<'de> for Plugin {

Ok(Self {
fetchers: shadow.fetchers,
spotters: shadow.spotters,
preloaders: shadow.preloaders,
previewers: shadow.previewers,
})
Expand Down
8 changes: 4 additions & 4 deletions yazi-config/src/plugin/preloader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ impl Preloader {
#[derive(Debug, Clone)]
pub struct PreloaderProps {
pub id: u8,
pub name: String,
pub name: &'static str,
pub prio: Priority,
}

impl From<&Preloader> for PreloaderProps {
fn from(preloader: &Preloader) -> Self {
Self { id: preloader.idx, name: preloader.run.name.to_owned(), prio: preloader.prio }
impl From<&'static Preloader> for PreloaderProps {
fn from(preloader: &'static Preloader) -> Self {
Self { id: preloader.idx, name: &preloader.run.name, prio: preloader.prio }
}
}
27 changes: 27 additions & 0 deletions yazi-config/src/plugin/spotter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use std::path::Path;

use serde::Deserialize;
use yazi_shared::{MIME_DIR, event::Cmd};

use crate::Pattern;

#[derive(Debug, Deserialize)]
pub struct Spotter {
pub name: Option<Pattern>,
pub mime: Option<Pattern>,
pub run: Cmd,
}

impl Spotter {
#[inline]
pub fn matches(&self, path: &Path, mime: &str) -> bool {
self.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|| self.name.as_ref().is_some_and(|p| p.match_path(path, mime == MIME_DIR))
}

#[inline]
pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) }

#[inline]
pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) }
}
2 changes: 1 addition & 1 deletion yazi-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
clippy::unit_arg
)]

yazi_macro::mod_pub!(completion confirm help input manager notify pick tab tasks which);
yazi_macro::mod_pub!(completion confirm help input manager notify pick spot tab tasks which);

pub fn init() {
manager::WATCHED.with(<_>::default);
Expand Down
1 change: 1 addition & 0 deletions yazi-core/src/manager/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ yazi_macro::mod_flat!(
remove
rename
seek
spot
suspend
tab_close
tab_create
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use yazi_shared::event::{Cmd, Data};

use crate::tab::Tab;
use crate::manager::Manager;

struct Opt {
skip: Option<usize>,
Expand All @@ -10,11 +10,14 @@ impl From<Cmd> for Opt {
fn from(c: Cmd) -> Self { Self { skip: c.first().and_then(Data::as_usize) } }
}

impl Tab {
impl Manager {
#[yazi_codegen::command]
pub fn spot(&mut self, c: Cmd) {
pub fn spot(&mut self, opt: Opt) {
let Some(hovered) = self.hovered().cloned() else {
return self.preview.reset();
return;
};

let mime = self.mimetype.get_owned(&hovered.url).unwrap_or_default();
self.active_mut().spot.go(hovered, mime.into());
}
}
4 changes: 2 additions & 2 deletions yazi-core/src/manager/commands/tab_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ impl Tabs {
if !opt.current {
tab.cd(opt.url);
} else if let Some(h) = self.active().hovered() {
tab.conf = self.active().conf.clone();
tab.pref = self.active().pref.clone();
tab.apply_files_attrs();
tab.reveal(h.url.to_regular());
} else {
tab.conf = self.active().conf.clone();
tab.pref = self.active().pref.clone();
tab.apply_files_attrs();
tab.cd(self.active().cwd().to_regular());
}
Expand Down
18 changes: 18 additions & 0 deletions yazi-core/src/spot/commands/arrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use yazi_shared::event::{Cmd, Data};

use crate::spot::Spot;

struct Opt {
step: isize,
}

impl From<Cmd> for Opt {
fn from(c: Cmd) -> Self { Self { step: c.first().and_then(Data::as_isize).unwrap_or(0) } }
}

impl Spot {
#[yazi_codegen::command]
pub fn arrow(&mut self, _: Opt) {
todo!();
}
}
21 changes: 21 additions & 0 deletions yazi-core/src/spot/commands/close.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use yazi_macro::render;
use yazi_shared::event::Cmd;

use crate::spot::Spot;

struct Opt;

impl From<Cmd> for Opt {
fn from(_: Cmd) -> Self { Self }
}
impl From<()> for Opt {
fn from(_: ()) -> Self { Self }
}

impl Spot {
#[yazi_codegen::command]
pub fn close(&mut self, _: Opt) {
self.ct.take().map(|h| h.cancel());
render!(self.lock.take().is_some());
}
}
1 change: 1 addition & 0 deletions yazi-core/src/spot/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yazi_macro::mod_flat!(arrow close);
3 changes: 3 additions & 0 deletions yazi-core/src/spot/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
yazi_macro::mod_pub!(commands);

yazi_macro::mod_flat!(spot);
Loading

0 comments on commit 5f58c6e

Please sign in to comment.