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" },
|
||||||
{ on = [ "N" ], exec = "find_arrow --previous" },
|
{ on = [ "N" ], exec = "find_arrow --previous" },
|
||||||
|
|
||||||
|
# Filter
|
||||||
|
{ on = [ "f" ], exec = "filter" },
|
||||||
|
|
||||||
# Sorting
|
# Sorting
|
||||||
{ on = [ ",", "m" ], exec = "sort modified --dir_first", desc = "Sort by modified time" },
|
{ 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)" },
|
{ 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_origin = "top-center"
|
||||||
search_offset = [ 0, 2, 50, 3 ]
|
search_offset = [ 0, 2, 50, 3 ]
|
||||||
|
|
||||||
|
# filter
|
||||||
|
filter_title = "Filter:"
|
||||||
|
filter_origin = "top-center"
|
||||||
|
filter_offset = [ 0, 2, 50, 3 ]
|
||||||
|
|
||||||
# shell
|
# shell
|
||||||
shell_title = [ "Shell:", "Shell (block):" ]
|
shell_title = [ "Shell:", "Shell (block):" ]
|
||||||
shell_origin = "top-center"
|
shell_origin = "top-center"
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,11 @@ pub struct Input {
|
||||||
pub find_origin: Origin,
|
pub find_origin: Origin,
|
||||||
pub find_offset: Offset,
|
pub find_offset: Offset,
|
||||||
|
|
||||||
|
// filter
|
||||||
|
pub filter_title: String,
|
||||||
|
pub filter_origin: Origin,
|
||||||
|
pub filter_offset: Offset,
|
||||||
|
|
||||||
// search
|
// search
|
||||||
pub search_title: String,
|
pub search_title: String,
|
||||||
pub search_origin: Origin,
|
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]
|
#[inline]
|
||||||
pub fn search() -> Self {
|
pub fn search() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ bitflags! {
|
||||||
const VISUAL = 0b0010;
|
const VISUAL = 0b0010;
|
||||||
const SELECT = 0b0100;
|
const SELECT = 0b0100;
|
||||||
const SEARCH = 0b1000;
|
const SEARCH = 0b1000;
|
||||||
|
const FILTER = 0b1001;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,6 +21,7 @@ impl From<&Exec> for Opt {
|
||||||
b"visual" => acc | Self::VISUAL,
|
b"visual" => acc | Self::VISUAL,
|
||||||
b"select" => acc | Self::SELECT,
|
b"select" => acc | Self::SELECT,
|
||||||
b"search" => acc | Self::SEARCH,
|
b"search" => acc | Self::SEARCH,
|
||||||
|
b"filter" => acc | Self::FILTER,
|
||||||
_ => acc,
|
_ => acc,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -45,13 +47,23 @@ impl Tab {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn escape_search(&mut self) -> bool { self.search_stop() }
|
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 {
|
pub fn escape(&mut self, opt: impl Into<Opt>) -> bool {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
if opt.is_empty() {
|
if opt.is_empty() {
|
||||||
return self.escape_find()
|
return self.escape_find()
|
||||||
|| self.escape_visual()
|
|| self.escape_visual()
|
||||||
|| self.escape_select()
|
|| self.escape_select()
|
||||||
|| self.escape_search();
|
|| self.escape_search()
|
||||||
|
|| self.escape_filter();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut b = false;
|
let mut b = false;
|
||||||
|
|
@ -67,6 +79,9 @@ impl Tab {
|
||||||
if opt.contains(Opt::SEARCH) {
|
if opt.contains(Opt::SEARCH) {
|
||||||
b |= self.escape_search();
|
b |= self.escape_search();
|
||||||
}
|
}
|
||||||
|
if opt.contains(Opt::FILTER) {
|
||||||
|
b |= self.escape_filter();
|
||||||
|
}
|
||||||
b
|
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 copy;
|
||||||
mod enter;
|
mod enter;
|
||||||
mod escape;
|
mod escape;
|
||||||
|
mod filter;
|
||||||
mod find;
|
mod find;
|
||||||
mod hidden;
|
mod hidden;
|
||||||
mod jump;
|
mod jump;
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,10 @@ impl<'a> Executor<'a> {
|
||||||
on!(ACTIVE, search);
|
on!(ACTIVE, search);
|
||||||
on!(ACTIVE, jump);
|
on!(ACTIVE, jump);
|
||||||
|
|
||||||
|
// Filter
|
||||||
|
on!(ACTIVE, filter);
|
||||||
|
on!(ACTIVE, filter_do);
|
||||||
|
|
||||||
// Find
|
// Find
|
||||||
on!(ACTIVE, find);
|
on!(ACTIVE, find);
|
||||||
on!(ACTIVE, find_do);
|
on!(ACTIVE, find_do);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue