diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index 5ab3a8ed..82f9cd22 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -1,21 +1,26 @@ -use std::{path::Path, process::Stdio}; +use std::process::Stdio; 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_shared::url::{AsUrl, UrlBuf, UrlLike}; use yazi_vfs::VfsFile; pub struct RgOpt { - pub cwd: UrlBuf, - pub hidden: bool, + pub cwd: UrlBuf, + pub hidden: bool, pub subject: String, - pub args: Vec, + pub args: Vec, } pub fn rg(opt: RgOpt) -> Result> { 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" }) .args(opt.args) .arg(opt.subject) @@ -29,18 +34,61 @@ pub fn rg(opt: RgOpt) -> Result> { let (tx, rx) = mpsc::unbounded_channel(); tokio::spawn(async move { - while let Ok(Some(line)) = it.next_line().await { - if Path::new(&line).is_absolute() { - continue; + // let mut occurrences_per_file: HashMap> = HashMap::new(); + // let mut current_file_path: String = String::new(); + 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(), ¤t_file, ¤t_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; - }; - if let Ok(file) = File::new(url).await { - tx.send(file).ok(); + + current_occurrences.push((line as u32, col as u32)); + } + + if current_occurrences.len() > 0 { + match build_file_url(opt.cwd, ¤t_file, ¤t_occurrences) { + Some(url) => { + if let Ok(file) = File::new(url).await { + tx.send(file).ok(); + } + } + None => {} } } + child.wait().await.ok(); }); + Ok(rx) } + +fn build_file_url(cwd: UrlBuf, file_path: &str, occurences: &Vec<(u32, u32)>) -> Option { + let occurrences_str = + occurences.iter().map(|(line, col)| format!("{line}-{col}")).collect::>().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()?)) +}