Compare commits

...

2 Commits

Author SHA1 Message Date
三咲雅 · Misaki Masa
2d3512e965
fix: panic caused by set_hook (#459) 2023-12-28 03:32:39 +08:00
Rolv Apneseth
d2599b80b0
feat: filter files in real-time (#454) 2023-12-28 00:33:16 +08:00
26 changed files with 327 additions and 152 deletions

View File

@ -1,4 +1,4 @@
use std::{env, path::Path, sync::{atomic::Ordering, Arc}};
use std::{env, path::Path, sync::Arc};
use anyhow::{anyhow, Result};
use ratatui::prelude::Rect;

View File

@ -89,6 +89,9 @@ keymap = [
{ on = [ "c", "f" ], exec = "copy filename", desc = "Copy the name of the file" },
{ on = [ "c", "n" ], exec = "copy name_without_ext", desc = "Copy the name of the file without the extension" },
# Filter
{ on = [ "f" ], exec = "filter" },
# Find
{ on = [ "/" ], exec = "find --smart" },
{ on = [ "?" ], exec = "find --previous --smart" },

View File

@ -139,6 +139,11 @@ delete_title = "Delete {n} selected file{s} permanently? (y/N)"
delete_origin = "top-center"
delete_offset = [ 0, 2, 50, 3 ]
# filter
filter_title = "Filter:"
filter_origin = "top-center"
filter_offset = [ 0, 2, 50, 3 ]
# find
find_title = [ "Find next:", "Find previous:" ]
find_origin = "top-center"

View File

@ -30,6 +30,11 @@ pub struct Input {
pub delete_origin: Origin,
pub delete_offset: Offset,
// filter
pub filter_title: String,
pub filter_origin: Origin,
pub filter_offset: Offset,
// find
pub find_title: [String; 2],
pub find_origin: Origin,

View File

@ -67,6 +67,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 find(prev: bool) -> Self {
Self {

View File

@ -5,7 +5,7 @@ use tokio::{fs, select, sync::mpsc::{self, UnboundedReceiver}};
use yazi_config::{manager::SortBy, MANAGER};
use yazi_shared::fs::{File, Url, FILES_TICKET};
use super::FilesSorter;
use super::{FilesSorter, Filter};
pub struct Files {
hidden: Vec<File>,
@ -18,6 +18,7 @@ pub struct Files {
selected: BTreeSet<Url>,
sorter: FilesSorter,
filter: Option<Filter>,
show_hidden: bool,
}
@ -34,6 +35,7 @@ impl Default for Files {
selected: Default::default(),
sorter: Default::default(),
filter: Default::default(),
show_hidden: MANAGER.show_hidden,
}
}
@ -136,11 +138,7 @@ impl Files {
self.ticket = FILES_TICKET.fetch_add(1, Ordering::Relaxed);
self.revision += 1;
(self.hidden, self.items) = if self.show_hidden {
(vec![], files)
} else {
files.into_iter().partition(|f| f.is_hidden())
};
(self.hidden, self.items) = self.split_files(files);
}
pub fn update_part(&mut self, files: Vec<File>, ticket: u64) {
@ -150,14 +148,9 @@ impl Files {
}
self.revision += 1;
if self.show_hidden {
self.hidden.clear();
self.items.extend(files);
} else {
let (hidden, items): (Vec<_>, Vec<_>) = files.into_iter().partition(|f| f.is_hidden());
self.hidden.extend(hidden);
self.items.extend(items);
}
let (hidden, items) = self.split_files(files);
self.hidden.extend(hidden);
self.items.extend(items);
return;
}
@ -200,12 +193,7 @@ impl Files {
};
}
let (hidden, items) = if self.show_hidden {
(vec![], files)
} else {
files.into_iter().partition(|f| f.is_hidden())
};
let (hidden, items) = self.split_files(files);
if !items.is_empty() {
go!(self.items, items);
}
@ -231,8 +219,15 @@ impl Files {
};
}
let (hidden, items) =
if self.show_hidden { (vec![], urls) } else { urls.into_iter().partition(|u| u.is_hidden()) };
let (hidden, items) = if let Some(filter) = &self.filter {
urls.into_iter().partition(|u| {
(!self.show_hidden && u.is_hidden()) || !u.file_name().is_some_and(|s| filter.matches(s))
})
} else if self.show_hidden {
(vec![], urls)
} else {
urls.into_iter().partition(|u| u.is_hidden())
};
if !items.is_empty() {
go!(self.items, items);
@ -242,7 +237,10 @@ impl Files {
}
}
pub fn update_updating(&mut self, files: BTreeMap<Url, File>) -> [BTreeMap<Url, File>; 2] {
pub fn update_updating(
&mut self,
files: BTreeMap<Url, File>,
) -> (BTreeMap<Url, File>, BTreeMap<Url, File>) {
if files.is_empty() {
return Default::default();
}
@ -264,7 +262,12 @@ impl Files {
};
}
let (mut hidden, mut items) = if self.show_hidden {
let (mut hidden, mut items) = if let Some(filter) = &self.filter {
files.into_iter().partition(|(_, f)| {
(f.is_hidden() && !self.show_hidden)
|| !f.url.file_name().is_some_and(|s| filter.matches(s))
})
} else if self.show_hidden {
(BTreeMap::new(), files)
} else {
files.into_iter().partition(|(_, f)| f.is_hidden())
@ -276,7 +279,7 @@ impl Files {
if !hidden.is_empty() {
go!(self.hidden, hidden);
}
[hidden, items]
(hidden, items)
}
pub fn update_upserting(&mut self, files: BTreeMap<Url, File>) {
@ -284,7 +287,7 @@ impl Files {
return;
}
let [hidden, items] = self.update_updating(files);
let (hidden, items) = self.update_updating(files);
if hidden.is_empty() && items.is_empty() {
return;
}
@ -307,6 +310,19 @@ impl Files {
self.sorter.sort(&mut self.items, &self.sizes);
true
}
fn split_files(&self, files: impl IntoIterator<Item = File>) -> (Vec<File>, Vec<File>) {
if let Some(filter) = &self.filter {
files.into_iter().partition(|f| {
(f.is_hidden() && !self.show_hidden)
|| !f.url.file_name().is_some_and(|s| filter.matches(s))
})
} else if self.show_hidden {
(vec![], files.into_iter().collect())
} else {
files.into_iter().partition(|f| f.is_hidden())
}
}
}
impl Files {
@ -374,6 +390,31 @@ impl Files {
}
}
// --- Filter
pub fn set_filter(&mut self, filter: Option<Filter>) -> bool {
if self.filter == filter {
return false;
}
self.filter = filter;
if self.filter.is_none() {
let take = mem::take(&mut self.hidden);
let (hidden, items) = self.split_files(take);
self.hidden = hidden;
if !items.is_empty() {
self.items.extend(items);
self.sorter.sort(&mut self.items, &self.sizes);
}
return true;
}
let it = mem::take(&mut self.items).into_iter().chain(mem::take(&mut self.hidden));
(self.hidden, self.items) = self.split_files(it);
self.sorter.sort(&mut self.items, &self.sizes);
true
}
// --- Show hidden
pub fn set_show_hidden(&mut self, state: bool) {
if self.show_hidden == state {
@ -387,12 +428,12 @@ impl Files {
return;
}
let take =
if self.show_hidden { mem::take(&mut self.hidden) } else { mem::take(&mut self.items) };
let (hidden, items) = self.split_files(take);
self.revision += 1;
if self.show_hidden {
self.items.append(&mut self.hidden);
} else {
let items = mem::take(&mut self.items);
(self.hidden, self.items) = items.into_iter().partition(|f| f.is_hidden());
}
self.hidden.extend(hidden);
self.items.extend(items);
}
}

View File

@ -0,0 +1,54 @@
use std::{ffi::OsStr, ops::Range};
use anyhow::Result;
use regex::bytes::{Regex, RegexBuilder};
use yazi_shared::event::Exec;
pub struct Filter {
raw: String,
regex: Regex,
}
impl PartialEq for Filter {
fn eq(&self, other: &Self) -> bool { self.raw == other.raw }
}
impl Filter {
pub fn new(s: &str, case: FilterCase) -> Result<Self> {
let regex = match case {
FilterCase::Smart => {
let uppercase = s.chars().any(|c| c.is_uppercase());
RegexBuilder::new(s).case_insensitive(!uppercase).build()?
}
FilterCase::Sensitive => Regex::new(s)?,
FilterCase::Insensitive => RegexBuilder::new(s).case_insensitive(true).build()?,
};
Ok(Self { raw: s.to_owned(), regex })
}
#[inline]
pub fn matches(&self, name: &OsStr) -> bool { self.regex.is_match(name.as_encoded_bytes()) }
#[inline]
pub fn highlighted(&self, name: &OsStr) -> Option<Vec<Range<usize>>> {
self.regex.find(name.as_encoded_bytes()).map(|m| vec![m.range()])
}
}
#[derive(Default, PartialEq, Eq)]
pub enum FilterCase {
Smart,
#[default]
Sensitive,
Insensitive,
}
impl From<&Exec> for FilterCase {
fn from(e: &Exec) -> Self {
match (e.named.contains_key("smart"), e.named.contains_key("insensitive")) {
(true, _) => Self::Smart,
(_, false) => Self::Sensitive,
(_, true) => Self::Insensitive,
}
}
}

View File

@ -1,7 +1,9 @@
mod files;
mod filter;
mod folder;
mod sorter;
pub use files::*;
pub use filter::*;
pub use folder::*;
pub use sorter::*;

View File

@ -5,10 +5,11 @@ use crate::tab::{Mode, Tab};
bitflags! {
pub struct Opt: u8 {
const FIND = 0b0001;
const VISUAL = 0b0010;
const SELECT = 0b0100;
const SEARCH = 0b1000;
const FIND = 0b00001;
const VISUAL = 0b00010;
const SELECT = 0b00100;
const FILTER = 0b01000;
const SEARCH = 0b10000;
}
}
@ -19,6 +20,7 @@ impl From<&Exec> for Opt {
b"find" => acc | Self::FIND,
b"visual" => acc | Self::VISUAL,
b"select" => acc | Self::SELECT,
b"filter" => acc | Self::FILTER,
b"search" => acc | Self::SEARCH,
_ => acc,
})
@ -42,6 +44,11 @@ impl Tab {
#[inline]
fn escape_select(&mut self) -> bool { self.select_all(Some(false)) }
#[inline]
fn escape_filter(&mut self) -> bool {
self.filter_do(super::filter::Opt { query: "", ..Default::default() })
}
#[inline]
fn escape_search(&mut self) -> bool { self.search_stop() }
@ -51,6 +58,7 @@ impl Tab {
return self.escape_find()
|| self.escape_visual()
|| self.escape_select()
|| self.escape_filter()
|| self.escape_search();
}
@ -64,6 +72,9 @@ impl Tab {
if opt.contains(Opt::SELECT) {
b |= self.escape_select();
}
if opt.contains(Opt::FILTER) {
b |= self.escape_filter();
}
if opt.contains(Opt::SEARCH) {
b |= self.escape_search();
}

View File

@ -0,0 +1,65 @@
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::{folder::{Filter, FilterCase}, input::Input, manager::Manager, tab::Tab};
#[derive(Default)]
pub struct Opt<'a> {
pub query: &'a str,
pub case: FilterCase,
}
impl<'a> From<&'a Exec> for Opt<'a> {
fn from(e: &'a Exec) -> Self {
Self { query: e.args.first().map(|s| s.as_str()).unwrap_or_default(), case: e.into() }
}
}
impl Tab {
pub fn filter<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
let opt = opt.into() as Opt;
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])
.with_bool("smart", opt.case == FilterCase::Smart)
.with_bool("insensitive", opt.case == FilterCase::Insensitive)
.vec(),
Layer::Manager
));
}
});
false
}
pub fn filter_do<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
let opt = opt.into() as Opt;
let filter = if opt.query.is_empty() {
None
} else if let Ok(f) = Filter::new(opt.query, opt.case) {
Some(f)
} else {
return false;
};
let hovered = self.current.hovered().map(|f| f.url());
if !self.current.files.set_filter(filter) {
return false;
}
if self.current.repos(hovered) {
Manager::_hover(None);
}
true
}
}

View File

@ -5,12 +5,12 @@ 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::{Finder, FinderCase, Tab}};
use crate::{folder::FilterCase, input::Input, tab::{Finder, Tab}};
pub struct Opt<'a> {
query: Option<&'a str>,
prev: bool,
case: FinderCase,
case: FilterCase,
}
impl<'a> From<&'a Exec> for Opt<'a> {
@ -18,11 +18,7 @@ impl<'a> From<&'a Exec> for Opt<'a> {
Self {
query: e.args.first().map(|s| s.as_str()),
prev: e.named.contains_key("previous"),
case: match (e.named.contains_key("smart"), e.named.contains_key("insensitive")) {
(true, _) => FinderCase::Smart,
(_, false) => FinderCase::Sensitive,
(_, true) => FinderCase::Insensitive,
},
case: e.into(),
}
}
}
@ -48,8 +44,8 @@ impl Tab {
emit!(Call(
Exec::call("find_do", vec![s])
.with_bool("previous", opt.prev)
.with_bool("smart", opt.case == FinderCase::Smart)
.with_bool("insensitive", opt.case == FinderCase::Insensitive)
.with_bool("smart", opt.case == FilterCase::Smart)
.with_bool("insensitive", opt.case == FilterCase::Insensitive)
.vec(),
Layer::Manager
));
@ -63,10 +59,16 @@ impl Tab {
let Some(query) = opt.query else {
return false;
};
if query.is_empty() {
return self.escape(super::escape::Opt::FIND);
}
let Ok(finder) = Finder::new(query, opt.case) else {
return false;
};
if matches!(&self.finder, Some(f) if f.filter == finder.filter) {
return false;
}
let step = if opt.prev {
finder.prev(&self.current.files, self.current.cursor, true)

View File

@ -4,6 +4,7 @@ mod cd;
mod copy;
mod enter;
mod escape;
mod filter;
mod find;
mod hidden;
mod jump;

View File

@ -1,41 +1,25 @@
use std::{collections::BTreeMap, ffi::OsStr, ops::Range};
use std::collections::BTreeMap;
use anyhow::Result;
use regex::bytes::{Regex, RegexBuilder};
use yazi_shared::fs::Url;
use crate::folder::Files;
#[derive(PartialEq, Eq)]
pub enum FinderCase {
Smart,
Sensitive,
Insensitive,
}
use crate::folder::{Files, Filter, FilterCase};
pub struct Finder {
query: Regex,
matched: BTreeMap<Url, u8>,
revision: u64,
pub filter: Filter,
matched: BTreeMap<Url, u8>,
revision: u64,
}
impl Finder {
pub(super) fn new(s: &str, case: FinderCase) -> Result<Self> {
let query = match case {
FinderCase::Smart => {
let uppercase = s.chars().any(|c| c.is_uppercase());
RegexBuilder::new(s).case_insensitive(!uppercase).build()?
}
FinderCase::Sensitive => Regex::new(s)?,
FinderCase::Insensitive => RegexBuilder::new(s).case_insensitive(true).build()?,
};
Ok(Self { query, matched: Default::default(), revision: 0 })
pub(super) fn new(s: &str, case: FilterCase) -> Result<Self> {
Ok(Self { filter: Filter::new(s, case)?, matched: Default::default(), revision: 0 })
}
pub(super) fn prev(&self, files: &Files, cursor: usize, include: bool) -> Option<isize> {
for i in !include as usize..files.len() {
let idx = (cursor + files.len() - i) % files.len();
if files[idx].name().is_some_and(|n| self.matches(n)) {
if files[idx].name().is_some_and(|n| self.filter.matches(n)) {
return Some(idx as isize - cursor as isize);
}
}
@ -45,7 +29,7 @@ impl Finder {
pub(super) fn next(&self, files: &Files, cursor: usize, include: bool) -> Option<isize> {
for i in !include as usize..files.len() {
let idx = (cursor + i) % files.len();
if files[idx].name().is_some_and(|n| self.matches(n)) {
if files[idx].name().is_some_and(|n| self.filter.matches(n)) {
return Some(idx as isize - cursor as isize);
}
}
@ -60,7 +44,7 @@ impl Finder {
let mut i = 0u8;
for file in files.iter() {
if file.name().map(|n| self.matches(n)) != Some(true) {
if file.name().map(|n| self.filter.matches(n)) != Some(true) {
continue;
}
@ -75,15 +59,6 @@ impl Finder {
self.revision = files.revision;
true
}
#[inline]
fn matches(&self, name: &OsStr) -> bool { self.query.is_match(name.as_encoded_bytes()) }
/// Explode the name into three parts: head, body, tail.
#[inline]
pub fn highlighted(&self, name: &OsStr) -> Option<Vec<Range<usize>>> {
self.query.find(name.as_encoded_bytes()).map(|m| vec![m.range()])
}
}
impl Finder {

View File

@ -23,6 +23,7 @@ better-panic = "^0"
crossterm = { version = "^0", features = [ "event-stream" ] }
fdlimit = "^0"
futures = "^0"
mlua = { version = "^0", features = [ "lua54", "vendored" ] }
ratatui = "^0"
tokio = { version = "^1", features = [ "parking_lot" ] }
unicode-width = "^0"
@ -36,12 +37,6 @@ tracing-subscriber = "^0"
libc = "^0"
signal-hook-tokio = { version = "^0", features = [ "futures-v0_3" ] }
[target.'cfg(any(target_arch="riscv64", target_arch="loongarch64"))'.dependencies]
mlua = { version = "^0", features = [ "lua52", "vendored" ] }
[target.'cfg(not(any(target_arch="riscv64", target_arch="loongarch64")))'.dependencies]
mlua = { version = "^0", features = [ "luajit52", "vendored" ] }
[[bin]]
name = "yazi"
path = "src/main.rs"

View File

@ -1,13 +1,10 @@
use std::sync::atomic::Ordering;
use anyhow::{Ok, Result};
use crossterm::event::KeyEvent;
use ratatui::backend::Backend;
use yazi_config::{keymap::Key, ARGS};
use yazi_core::input::InputMode;
use yazi_shared::{emit, event::{Event, Exec}, term::Term, Layer, COLLISION};
use yazi_shared::{emit, event::{Event, Exec}, term::Term, Layer};
use crate::{lives::Lives, Ctx, Executor, Logs, Panic, Root, Signals};
use crate::{lives::Lives, Ctx, Executor, Logs, Panic, Signals};
pub(crate) struct App {
pub(crate) cx: Ctx,
@ -26,7 +23,7 @@ impl App {
Lives::register()?;
let mut app = Self { cx: Ctx::make(), term: Some(term), signals };
app.dispatch_render()?;
app.render()?;
while let Some(event) = app.signals.recv().await {
match event {
Event::Quit(no_cwd_file) => {
@ -35,7 +32,7 @@ impl App {
}
Event::Key(key) => app.dispatch_key(key),
Event::Paste(str) => app.dispatch_paste(str),
Event::Render(_) => app.dispatch_render()?,
Event::Render(_) => app.render()?,
Event::Resize(cols, rows) => app.dispatch_resize(cols, rows)?,
Event::Call(exec, layer) => app.dispatch_call(exec, layer),
event => app.dispatch_module(event),
@ -68,51 +65,9 @@ impl App {
}
}
fn dispatch_render(&mut self) -> Result<()> {
let Some(term) = &mut self.term else {
return Ok(());
};
let collision = COLLISION.swap(false, Ordering::Relaxed);
let frame = term.draw(|f| {
Lives::scope(&self.cx, |_| {
f.render_widget(Root::new(&self.cx), f.size());
});
if let Some((x, y)) = self.cx.cursor() {
f.set_cursor(x, y);
}
})?;
if !COLLISION.load(Ordering::Relaxed) {
if collision {
// Reload preview if collision is resolved
self.cx.manager.peek(true);
}
return Ok(());
}
let mut patches = Vec::new();
for x in frame.area.left()..frame.area.right() {
for y in frame.area.top()..frame.area.bottom() {
let cell = frame.buffer.get(x, y);
if cell.skip {
patches.push((x, y, cell.clone()));
}
}
}
term.backend_mut().draw(patches.iter().map(|(x, y, cell)| (*x, *y, cell)))?;
if let Some((x, y)) = self.cx.cursor() {
term.show_cursor()?;
term.set_cursor(x, y)?;
}
term.backend_mut().flush()?;
Ok(())
}
fn dispatch_resize(&mut self, _: u16, _: u16) -> Result<()> {
self.cx.manager.active_mut().preview.reset();
self.dispatch_render()?;
self.render()?;
self.cx.manager.current_mut().set_page(true);
self.cx.manager.peek(false);
@ -127,7 +82,6 @@ impl App {
}
fn dispatch_module(&mut self, event: Event) {
let manager = &mut self.cx.manager;
let tasks = &mut self.cx.tasks;
match event {
Event::Pages(page) => {

View File

@ -1,2 +1,3 @@
mod plugin;
mod render;
mod stop;

View File

@ -0,0 +1,51 @@
use std::sync::atomic::Ordering;
use anyhow::Result;
use ratatui::backend::Backend;
use yazi_shared::COLLISION;
use crate::{app::App, lives::Lives, root::Root};
impl App {
pub(crate) fn render(&mut self) -> Result<()> {
let Some(term) = &mut self.term else {
return Ok(());
};
let collision = COLLISION.swap(false, Ordering::Relaxed);
let frame = term.draw(|f| {
Lives::scope(&self.cx, |_| {
f.render_widget(Root::new(&self.cx), f.size());
});
if let Some((x, y)) = self.cx.cursor() {
f.set_cursor(x, y);
}
})?;
if !COLLISION.load(Ordering::Relaxed) {
if collision {
// Reload preview if collision is resolved
self.cx.manager.peek(true);
}
return Ok(());
}
let mut patches = Vec::new();
for x in frame.area.left()..frame.area.right() {
for y in frame.area.top()..frame.area.bottom() {
let cell = frame.buffer.get(x, y);
if cell.skip {
patches.push((x, y, cell.clone()));
}
}
}
term.backend_mut().draw(patches.iter().map(|(x, y, cell)| (*x, *y, cell)))?;
if let Some((x, y)) = self.cx.cursor() {
term.show_cursor()?;
term.set_cursor(x, y)?;
}
term.backend_mut().flush()?;
Ok(())
}
}

View File

@ -1,6 +1,6 @@
use anyhow::Result;
use tokio::sync::oneshot;
use yazi_shared::{emit, event::Exec, term::Term};
use yazi_shared::{event::Exec, term::Term};
use crate::app::App;
@ -30,9 +30,10 @@ impl App {
} else {
self.term = Some(Term::start().unwrap());
self.signals.stop_term(false);
// FIXME: find a better way to handle this
self.render().unwrap();
self.cx.manager.hover(None);
self.cx.manager.peek(true);
emit!(Render);
}
if let Some(tx) = opt.tx {
tx.send(()).ok();

View File

@ -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);

View File

@ -2,7 +2,7 @@ use ratatui::{buffer::Buffer, layout::{self, Rect}, prelude::{Constraint, Direct
use yazi_config::THEME;
use super::Bindings;
use crate::{Ctx, widgets};
use crate::{widgets, Ctx};
pub(crate) struct Layout<'a> {
cx: &'a Ctx,

View File

@ -6,7 +6,7 @@ use yazi_config::THEME;
use yazi_core::input::InputMode;
use yazi_shared::term::Term;
use crate::{Ctx, widgets};
use crate::{widgets, Ctx};
pub(crate) struct Input<'a> {
cx: &'a Ctx,

View File

@ -147,7 +147,7 @@ impl<'a, 'b> Folder<'a, 'b> {
};
let file = me.borrow::<yazi_shared::fs::File>()?;
let Some(h) = file.name().and_then(|n| finder.highlighted(n)) else {
let Some(h) = file.name().and_then(|n| finder.filter.highlighted(n)) else {
return Ok(None);
};

View File

@ -1,7 +1,7 @@
use ratatui::{buffer::Buffer, layout::Rect, widgets::{Block, BorderType, Borders, List, ListItem, Widget}};
use yazi_config::THEME;
use crate::{Ctx, widgets};
use crate::{widgets, Ctx};
pub(crate) struct Select<'a> {
cx: &'a Ctx,

View File

@ -2,7 +2,7 @@ use ratatui::{buffer::Buffer, layout::{self, Alignment, Constraint, Direction, R
use yazi_config::THEME;
use yazi_core::tasks::TASKS_PERCENT;
use crate::{Ctx, widgets};
use crate::{widgets, Ctx};
pub(crate) struct Layout<'a> {
cx: &'a Ctx,

View File

@ -2,7 +2,7 @@ use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, widgets::{
use yazi_config::THEME;
use super::Side;
use crate::{Ctx, widgets};
use crate::{widgets, Ctx};
pub(crate) struct Which<'a> {
cx: &'a Ctx,

View File

@ -20,6 +20,7 @@ crossterm = "^0"
futures = "^0"
libc = "^0"
md-5 = "^0"
mlua = { version = "^0", features = [ "lua54", "vendored", "serialize", "macros", "async" ] }
parking_lot = "^0"
ratatui = "^0"
serde = "^1"
@ -30,9 +31,3 @@ tokio-util = "^0"
tracing = { version = "^0", features = [ "max_level_debug", "release_max_level_warn" ] }
unicode-width = "^0"
yazi-prebuild = "^0"
[target.'cfg(any(target_arch="riscv64", target_arch="loongarch64"))'.dependencies]
mlua = { version = "^0", features = [ "lua52", "vendored", "serialize", "macros", "async" ] }
[target.'cfg(not(any(target_arch="riscv64", target_arch="loongarch64")))'.dependencies]
mlua = { version = "^0", features = [ "luajit52", "vendored", "serialize", "macros", "async" ] }