This commit is contained in:
sxyazi 2023-09-11 00:33:32 +08:00
parent 4d01119981
commit 2bbe04fe5d
No known key found for this signature in database
6 changed files with 132 additions and 16 deletions

12
Cargo.lock generated
View file

@ -177,9 +177,9 @@ dependencies = [
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.21.3" version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]] [[package]]
name = "bincode" name = "bincode"
@ -1548,9 +1548,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.105" version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -1880,9 +1880,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.7.7" version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0a3ab2091e52d7299a39d098e200114a972df0a7724add02a273aa9aada592" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
dependencies = [ dependencies = [
"indexmap 2.0.0", "indexmap 2.0.0",
"serde", "serde",

View file

@ -112,7 +112,7 @@ impl<'a> Widget for Folder<'a> {
if let Some(idx) = active if let Some(idx) = active
.finder() .finder()
.filter(|_| hovered && self.is_find) .filter(|&f| hovered && self.is_find && f.has_matched())
.and_then(|finder| finder.matched_idx(f.url())) .and_then(|finder| finder.matched_idx(f.url()))
{ {
let len = active.finder().unwrap().matched().len(); let len = active.finder().unwrap().matched().len();

View file

@ -24,18 +24,54 @@ impl Finder {
.take(cursor) .take(cursor)
.rev() .rev()
.enumerate() .enumerate()
.find(|(_, f)| f.name().map_or(false, |n| self.is_match(n))) .find(|(_, f)| f.name().map_or(false, |n| self.matches(n)))
.map(|(i, _)| -(i as isize) - 1) .map(|(i, _)| -(i as isize) - 1)
} else { } else {
files files
.iter() .iter()
.skip(cursor + 1) .skip(cursor + 1)
.enumerate() .enumerate()
.find(|(_, f)| f.name().map_or(false, |n| self.is_match(n))) .find(|(_, f)| f.name().map_or(false, |n| self.matches(n)))
.map(|(i, _)| i as isize + 1) .map(|(i, _)| i as isize + 1)
} }
} }
pub(super) fn ring(&self, files: &Files, cursor: usize, prev: bool) -> Option<isize> {
if prev {
files
.iter()
.take(cursor + 1)
.rev()
.enumerate()
.find(|(_, f)| f.name().map_or(false, |n| self.matches(n)))
.map(|(i, _)| -(i as isize))
.or_else(|| {
files
.iter()
.skip(cursor + 1)
.enumerate()
.find(|(_, f)| f.name().map_or(false, |n| self.matches(n)))
.map(|(i, _)| i as isize + 1)
})
} else {
files
.iter()
.skip(cursor)
.enumerate()
.find(|(_, f)| f.name().map_or(false, |n| self.matches(n)))
.map(|(i, _)| i as isize)
.or_else(|| {
files
.iter()
.take(cursor)
.rev()
.enumerate()
.find(|(_, f)| f.name().map_or(false, |n| self.matches(n)))
.map(|(i, _)| -(i as isize) - 1)
})
}
}
pub(super) fn catchup(&mut self, files: &Files) -> bool { pub(super) fn catchup(&mut self, files: &Files) -> bool {
if self.version == files.version() { if self.version == files.version() {
return false; return false;
@ -44,7 +80,7 @@ impl Finder {
let mut i = 0u8; let mut i = 0u8;
for file in files.iter() { for file in files.iter() {
if file.name().map(|n| self.is_match(n)) != Some(true) { if file.name().map(|n| self.matches(n)) != Some(true) {
continue; continue;
} }
@ -61,7 +97,7 @@ impl Finder {
} }
#[inline] #[inline]
fn is_match(&self, name: &OsStr) -> bool { fn matches(&self, name: &OsStr) -> bool {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
self.query.is_match(name.to_string_lossy().as_bytes()) self.query.is_match(name.to_string_lossy().as_bytes())
@ -78,12 +114,15 @@ impl Finder {
#[inline] #[inline]
pub fn matched(&self) -> &BTreeMap<Url, u8> { &self.matched } pub fn matched(&self) -> &BTreeMap<Url, u8> { &self.matched }
#[inline]
pub fn has_matched(&self) -> bool { !self.matched.is_empty() }
#[inline] #[inline]
pub fn matched_idx(&self, url: &Url) -> Option<u8> { pub fn matched_idx(&self, url: &Url) -> Option<u8> {
if let Some((_, &idx)) = self.matched.iter().find(|(u, _)| *u == url) { if let Some((_, &idx)) = self.matched.iter().find(|(u, _)| *u == url) {
return Some(idx); return Some(idx);
} }
if url.file_name().map(|n| self.is_match(n)) == Some(true) { if url.file_name().map(|n| self.matches(n)) == Some(true) {
return Some(100); return Some(100);
} }
None None

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}
use anyhow::{bail, Error, Result}; use anyhow::{bail, Error, Result};
use config::{keymap::{Exec, KeymapLayer}, open::Opener}; use config::{keymap::{Exec, KeymapLayer}, open::Opener};
use shared::{Defer, Url}; use shared::{Debounce, Defer, InputError, Url};
use tokio::{pin, task::JoinHandle}; use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
@ -241,12 +241,25 @@ impl Tab {
pub fn find(&mut self, query: Option<&str>, prev: bool) -> bool { pub fn find(&mut self, query: Option<&str>, prev: bool) -> bool {
if let Some(query) = query { if let Some(query) = query {
self.finder = Finder::new(query).ok(); let Ok(finder) = Finder::new(query) else {
return self.find_arrow(prev); return false;
};
if let Some(step) = finder.ring(&self.current.files, self.current.cursor(), prev) {
self.arrow(step);
}
self.finder = Some(finder);
return true;
} }
tokio::spawn(async move { tokio::spawn(async move {
if let Some(Ok(s)) = emit!(Input(InputOpt::top("Find:"))).recv().await { let rx = emit!(Input(InputOpt::top("Find:").with_realtime()));
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( emit!(Call(
Exec::call("find", vec![s]).with_bool("previous", prev).vec(), Exec::call("find", vec![s]).with_bool("previous", prev).vec(),
KeymapLayer::Manager KeymapLayer::Manager

62
shared/src/debounce.rs Normal file
View file

@ -0,0 +1,62 @@
use std::{pin::Pin, task::{Context, Poll}, time::Duration};
use futures::{FutureExt, Stream, StreamExt};
use tokio::time::{sleep, Instant, Sleep};
pub struct Debounce<S>
where
S: Stream,
{
stream: S,
interval: Duration,
sleep: Sleep,
last: Option<S::Item>,
}
impl<S> Debounce<S>
where
S: Stream + Unpin,
{
pub fn new(stream: S, interval: Duration) -> Debounce<S> {
Self { stream, interval, sleep: sleep(Duration::ZERO), last: None }
}
}
impl<S> Stream for Debounce<S>
where
S: Stream + Unpin,
{
type Item = S::Item;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let (mut stream, interval, mut sleep, last) = unsafe {
let me = self.get_unchecked_mut();
(Pin::new(&mut me.stream), me.interval, Pin::new_unchecked(&mut me.sleep), &mut me.last)
};
if sleep.poll_unpin(cx).is_ready() {
if let Some(last) = last.take() {
return Poll::Ready(Some(last));
}
}
while let Poll::Ready(next) = stream.poll_next_unpin(cx) {
match next {
Some(next) => {
*last = Some(next);
}
None if last.is_none() => {
return Poll::Ready(None);
}
None => {
sleep.reset(Instant::now());
return Poll::Pending;
}
}
}
sleep.reset(Instant::now() + interval);
Poll::Pending
}
}

View file

@ -1,6 +1,7 @@
#![allow(clippy::option_map_unit_fn)] #![allow(clippy::option_map_unit_fn)]
mod chars; mod chars;
mod debounce;
mod defer; mod defer;
mod errors; mod errors;
mod fns; mod fns;
@ -13,6 +14,7 @@ mod time;
mod url; mod url;
pub use chars::*; pub use chars::*;
pub use debounce::*;
pub use defer::*; pub use defer::*;
pub use errors::*; pub use errors::*;
pub use fns::*; pub use fns::*;