mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: add filter command
This commit is contained in:
parent
82bab0f24a
commit
9873559642
8 changed files with 103 additions and 1 deletions
|
|
@ -95,6 +95,9 @@ keymap = [
|
|||
{ on = [ "n" ], exec = "find_arrow" },
|
||||
{ on = [ "N" ], exec = "find_arrow --previous" },
|
||||
|
||||
# Filter
|
||||
{ on = [ "f" ], exec = "filter" },
|
||||
|
||||
# Sorting
|
||||
{ on = [ ",", "m" ], exec = "sort modified --dir_first", desc = "Sort by modified time" },
|
||||
{ on = [ ",", "M" ], exec = "sort modified --reverse --dir_first", desc = "Sort by modified time (reverse)" },
|
||||
|
|
|
|||
|
|
@ -149,6 +149,11 @@ search_title = "Search:"
|
|||
search_origin = "top-center"
|
||||
search_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
# filter
|
||||
filter_title = "Filter:"
|
||||
filter_origin = "top-center"
|
||||
filter_offset = [ 0, 2, 50, 3 ]
|
||||
|
||||
# shell
|
||||
shell_title = [ "Shell:", "Shell (block):" ]
|
||||
shell_origin = "top-center"
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ pub struct Input {
|
|||
pub find_origin: Origin,
|
||||
pub find_offset: Offset,
|
||||
|
||||
// filter
|
||||
pub filter_title: String,
|
||||
pub filter_origin: Origin,
|
||||
pub filter_offset: Offset,
|
||||
|
||||
// search
|
||||
pub search_title: String,
|
||||
pub search_origin: Origin,
|
||||
|
|
|
|||
|
|
@ -77,6 +77,16 @@ impl InputCfg {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn filter() -> Self {
|
||||
Self {
|
||||
title: INPUT.filter_title.to_owned(),
|
||||
position: Position::new(INPUT.filter_origin, INPUT.filter_offset),
|
||||
realtime: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn search() -> Self {
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ bitflags! {
|
|||
const VISUAL = 0b0010;
|
||||
const SELECT = 0b0100;
|
||||
const SEARCH = 0b1000;
|
||||
const FILTER = 0b1001;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ impl From<&Exec> for Opt {
|
|||
b"visual" => acc | Self::VISUAL,
|
||||
b"select" => acc | Self::SELECT,
|
||||
b"search" => acc | Self::SEARCH,
|
||||
b"filter" => acc | Self::FILTER,
|
||||
_ => acc,
|
||||
})
|
||||
}
|
||||
|
|
@ -45,13 +47,23 @@ impl Tab {
|
|||
#[inline]
|
||||
fn escape_search(&mut self) -> bool { self.search_stop() }
|
||||
|
||||
#[inline]
|
||||
fn escape_filter(&mut self) -> bool {
|
||||
if self.current.files.filter_keyword.is_some() {
|
||||
self.current.files.set_filter_keyword("");
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn escape(&mut self, opt: impl Into<Opt>) -> bool {
|
||||
let opt = opt.into() as Opt;
|
||||
if opt.is_empty() {
|
||||
return self.escape_find()
|
||||
|| self.escape_visual()
|
||||
|| self.escape_select()
|
||||
|| self.escape_search();
|
||||
|| self.escape_search()
|
||||
|| self.escape_filter();
|
||||
}
|
||||
|
||||
let mut b = false;
|
||||
|
|
@ -67,6 +79,9 @@ impl Tab {
|
|||
if opt.contains(Opt::SEARCH) {
|
||||
b |= self.escape_search();
|
||||
}
|
||||
if opt.contains(Opt::FILTER) {
|
||||
b |= self.escape_filter();
|
||||
}
|
||||
b
|
||||
}
|
||||
}
|
||||
|
|
|
|||
59
yazi-core/src/tab/commands/filter.rs
Normal file
59
yazi-core/src/tab/commands/filter.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use tokio::pin;
|
||||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||
use yazi_config::popup::InputCfg;
|
||||
use yazi_shared::{emit, event::Exec, Debounce, InputError, Layer};
|
||||
|
||||
use crate::{input::Input, tab::Tab};
|
||||
|
||||
pub struct Opt<'a> {
|
||||
query: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Exec> for Opt<'a> {
|
||||
fn from(e: &'a Exec) -> Self {
|
||||
Self { query: e.args.first().map(|s| s.as_str()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn filter<'a>(&mut self, _opt: impl Into<Opt<'a>>) -> bool {
|
||||
tokio::spawn(async move {
|
||||
let rx = Input::_show(InputCfg::filter());
|
||||
|
||||
let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50));
|
||||
pin!(rx);
|
||||
|
||||
while let Some(Ok(s)) | Some(Err(InputError::Typed(s))) = rx.next().await {
|
||||
emit!(Call(Exec::call("filter_do", vec![s]).vec(), Layer::Manager));
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
|
||||
pub fn filter_do<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
||||
let opt = opt.into() as Opt;
|
||||
let Some(query) = opt.query else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let hovered = &self.current.hovered().map(|f| f.url());
|
||||
|
||||
self.current.files.set_filter_keyword(query);
|
||||
|
||||
if let Some(hovered_url) = hovered {
|
||||
match (self.current.files.position(hovered_url), self.current.files.first()) {
|
||||
(Some(_), _) => {
|
||||
self.current.hover(hovered_url);
|
||||
}
|
||||
(None, Some(first)) => {
|
||||
self.current.hover(&first.url());
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ mod cd;
|
|||
mod copy;
|
||||
mod enter;
|
||||
mod escape;
|
||||
mod filter;
|
||||
mod find;
|
||||
mod hidden;
|
||||
mod jump;
|
||||
|
|
|
|||
|
|
@ -152,6 +152,10 @@ impl<'a> Executor<'a> {
|
|||
on!(ACTIVE, search);
|
||||
on!(ACTIVE, jump);
|
||||
|
||||
// Filter
|
||||
on!(ACTIVE, filter);
|
||||
on!(ACTIVE, filter_do);
|
||||
|
||||
// Find
|
||||
on!(ACTIVE, find);
|
||||
on!(ACTIVE, find_do);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue