feat: wip improve rg generated url to get all files occurences

This commit is contained in:
Jed 2026-06-04 19:49:37 +02:00
parent d9cd1907d9
commit c1f4fb5c3b

View file

@ -1,21 +1,26 @@
use std::{path::Path, process::Stdio}; use std::process::Stdio;
use anyhow::Result; use anyhow::Result;
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}}; use tokio::{
io::{AsyncBufReadExt, BufReader},
process::Command,
sync::mpsc::{self, UnboundedReceiver},
};
use yazi_fs::{File, FsUrl}; use yazi_fs::{File, FsUrl};
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike}; use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
use yazi_vfs::VfsFile; use yazi_vfs::VfsFile;
pub struct RgOpt { pub struct RgOpt {
pub cwd: UrlBuf, pub cwd: UrlBuf,
pub hidden: bool, pub hidden: bool,
pub subject: String, pub subject: String,
pub args: Vec<String>, pub args: Vec<String>,
} }
pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> { pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
let mut child = Command::new("rg") let mut child = Command::new("rg")
.args(["--color=never", "--files-with-matches", "--smart-case"]) .args(["--color=never", "--no-heading", "--column", "--smart-case"])
// .args(["--color=never", "--files-with-matches", "--smart-case"])
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" }) .arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
.args(opt.args) .args(opt.args)
.arg(opt.subject) .arg(opt.subject)
@ -29,18 +34,61 @@ pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
let (tx, rx) = mpsc::unbounded_channel(); let (tx, rx) = mpsc::unbounded_channel();
tokio::spawn(async move { tokio::spawn(async move {
while let Ok(Some(line)) = it.next_line().await { // let mut occurrences_per_file: HashMap<String, Vec<(u32, u32)>> = HashMap::new();
if Path::new(&line).is_absolute() { // let mut current_file_path: String = String::new();
continue; let mut current_file: String = String::new();
let mut current_occurrences: Vec<(u32, u32)> = vec![];
while let Ok(Some(search_line)) = it.next_line().await {
let Some((file_path, line, col)) = parse_rg_line(&search_line) else { continue };
if current_file != file_path {
let Some(url) = build_file_url(opt.cwd.clone(), &current_file, &current_occurrences) else {
continue;
};
if let Ok(file) = File::new(url).await {
tx.send(file).ok();
}
current_file = file_path.clone();
current_occurrences = vec![];
} }
let Ok(url) = opt.cwd.try_join(line) else {
continue; current_occurrences.push((line as u32, col as u32));
}; }
if let Ok(file) = File::new(url).await {
tx.send(file).ok(); if current_occurrences.len() > 0 {
match build_file_url(opt.cwd, &current_file, &current_occurrences) {
Some(url) => {
if let Ok(file) = File::new(url).await {
tx.send(file).ok();
}
}
None => {}
} }
} }
child.wait().await.ok(); child.wait().await.ok();
}); });
Ok(rx) Ok(rx)
} }
fn build_file_url(cwd: UrlBuf, file_path: &str, occurences: &Vec<(u32, u32)>) -> Option<UrlBuf> {
let occurrences_str =
occurences.iter().map(|(line, col)| format!("{line}-{col}")).collect::<Vec<_>>().join(",");
let url = cwd.try_join(file_path).ok().and_then(|u| u.into_search(occurrences_str).ok());
url
}
fn parse_rg_line(line: &str) -> Option<(String, usize, usize)> {
let mut parts = line.split(':');
let (file, line, col) = (parts.next()?, parts.next()?, parts.next()?);
if file.is_empty() {
return None;
}
Some((file.to_owned(), line.parse().ok()?, col.parse().ok()?))
}