yazi_core/tab/commands/
search.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::{borrow::Cow, mem, time::Duration};

use tokio::pin;
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
use tracing::error;
use yazi_config::popup::InputCfg;
use yazi_fs::{Cha, FilesOp};
use yazi_plugin::external;
use yazi_proxy::{AppProxy, InputProxy, ManagerProxy, TabProxy, options::{SearchOpt, SearchOptVia}};

use crate::tab::Tab;

impl Tab {
	pub fn search(&mut self, opt: impl TryInto<SearchOpt>) {
		let Ok(mut opt) = opt.try_into() else {
			return AppProxy::notify_error("Invalid `search` option", "Failed to parse search option");
		};

		if opt.via == SearchOptVia::None {
			return self.search_stop();
		}

		if let Some(handle) = self.search.take() {
			handle.abort();
		}

		tokio::spawn(async move {
			let mut input =
				InputProxy::show(InputCfg::search(&opt.via.to_string()).with_value(opt.subject));

			if let Some(Ok(subject)) = input.recv().await {
				opt.subject = Cow::Owned(subject);
				TabProxy::search_do(opt);
			}
		});
	}

	pub fn search_do(&mut self, opt: impl TryInto<SearchOpt>) {
		let Ok(opt) = opt.try_into() else {
			return error!("Failed to parse search option for `search_do`");
		};

		if let Some(handle) = self.search.take() {
			handle.abort();
		}

		let cwd = self.cwd().to_search(&opt.subject);
		let hidden = self.pref.show_hidden;

		self.search = Some(tokio::spawn(async move {
			let rx = if opt.via == SearchOptVia::Rg {
				external::rg(external::RgOpt {
					cwd: cwd.clone(),
					hidden,
					subject: opt.subject.into_owned(),
					args: opt.args,
				})
			} else {
				external::fd(external::FdOpt {
					cwd: cwd.clone(),
					hidden,
					subject: opt.subject.into_owned(),
					args: opt.args,
				})
			}?;

			let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300));
			pin!(rx);

			let ((), ticket) = (TabProxy::cd(&cwd), FilesOp::prepare(&cwd));
			while let Some(chunk) = rx.next().await {
				FilesOp::Part(cwd.clone(), chunk, ticket).emit();
			}
			FilesOp::Done(cwd, Cha::dummy(), ticket).emit();

			Ok(())
		}));
	}

	pub(super) fn search_stop(&mut self) {
		if let Some(handle) = self.search.take() {
			handle.abort();
		}
		if self.cwd().is_search() {
			let rep = self.history.remove_or(&self.cwd().to_regular());
			drop(mem::replace(&mut self.current, rep));
			ManagerProxy::refresh();
		}
	}
}