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

View file

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

View file

@ -24,18 +24,54 @@ impl Finder {
.take(cursor)
.rev()
.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)
} else {
files
.iter()
.skip(cursor + 1)
.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)
}
}
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 {
if self.version == files.version() {
return false;
@ -44,7 +80,7 @@ impl Finder {
let mut i = 0u8;
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;
}
@ -61,7 +97,7 @@ impl Finder {
}
#[inline]
fn is_match(&self, name: &OsStr) -> bool {
fn matches(&self, name: &OsStr) -> bool {
#[cfg(target_os = "windows")]
{
self.query.is_match(name.to_string_lossy().as_bytes())
@ -78,12 +114,15 @@ impl Finder {
#[inline]
pub fn matched(&self) -> &BTreeMap<Url, u8> { &self.matched }
#[inline]
pub fn has_matched(&self) -> bool { !self.matched.is_empty() }
#[inline]
pub fn matched_idx(&self, url: &Url) -> Option<u8> {
if let Some((_, &idx)) = self.matched.iter().find(|(u, _)| *u == url) {
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);
}
None

View file

@ -2,7 +2,7 @@ use std::{borrow::Cow, collections::{BTreeMap, BTreeSet}, ffi::{OsStr, OsString}
use anyhow::{bail, Error, Result};
use config::{keymap::{Exec, KeymapLayer}, open::Opener};
use shared::{Defer, Url};
use shared::{Debounce, Defer, InputError, Url};
use tokio::{pin, task::JoinHandle};
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
@ -241,12 +241,25 @@ impl Tab {
pub fn find(&mut self, query: Option<&str>, prev: bool) -> bool {
if let Some(query) = query {
self.finder = Finder::new(query).ok();
return self.find_arrow(prev);
let Ok(finder) = Finder::new(query) else {
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 {
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(
Exec::call("find", vec![s]).with_bool("previous", prev).vec(),
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)]
mod chars;
mod debounce;
mod defer;
mod errors;
mod fns;
@ -13,6 +14,7 @@ mod time;
mod url;
pub use chars::*;
pub use debounce::*;
pub use defer::*;
pub use errors::*;
pub use fns::*;