cosmic-edit/src/search.rs
2025-11-10 07:44:27 -07:00

118 lines
4.7 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-only
use grep::matcher::{Match, Matcher};
use grep::regex::RegexMatcher;
use grep::searcher::{Searcher, sinks::UTF8};
use std::path::PathBuf;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LineSearchResult {
pub number: usize,
pub text: String,
pub first: Match,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FileSearchResult {
pub path: PathBuf,
pub lines: Vec<LineSearchResult>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProjectSearchResult {
//TODO: should this be included?
pub value: String,
pub in_progress: bool,
pub files: Vec<FileSearchResult>,
}
impl ProjectSearchResult {
pub fn search_projects(&mut self, projects: Vec<(String, PathBuf)>) {
//TODO: support literal search
//TODO: use ignore::WalkParallel?
match RegexMatcher::new(&self.value) {
Ok(matcher) => {
let mut searcher = Searcher::new();
let mut walk_builder_opt: Option<ignore::WalkBuilder> = None;
for (_, project_path) in projects.iter() {
walk_builder_opt = match walk_builder_opt.take() {
Some(mut walk_builder) => {
walk_builder.add(project_path);
Some(walk_builder)
}
None => Some(ignore::WalkBuilder::new(project_path)),
};
}
if let Some(walk_builder) = walk_builder_opt {
for entry_res in walk_builder.build() {
let entry = match entry_res {
Ok(ok) => ok,
Err(err) => {
log::error!("failed to walk projects {:?}: {}", projects, err);
continue;
}
};
if let Some(file_type) = entry.file_type() {
if file_type.is_dir() {
continue;
}
}
let entry_path = entry.path();
let mut lines = Vec::new();
match searcher.search_path(
&matcher,
entry_path,
UTF8(|number_u64, text| {
match usize::try_from(number_u64) {
Ok(number) => match matcher.find(text.as_bytes()) {
Ok(Some(first)) => {
lines.push(LineSearchResult {
number,
text: text.trim_end().to_string(),
first,
});
},
Ok(None) => {
log::error!("first match in file {:?} line {} not found", entry_path, number);
}
Err(err) => {
log::error!("failed to find first match in file {:?} line {}: {}", entry_path, number, err);
}
},
Err(err) => {
log::error!("failed to convert file {:?} line {} to usize: {}", entry_path, number_u64, err);
}
}
Ok(true)
}),
) {
Ok(()) => {
if !lines.is_empty() {
self.files.push(FileSearchResult {
path: entry_path.to_path_buf(),
lines,
});
}
}
Err(err) => {
log::error!("failed to search file {:?}: {}", entry_path, err);
}
}
}
}
}
Err(err) => {
log::error!(
"failed to create regex matcher with value {:?}: {}",
self.value,
err
);
}
}
self.in_progress = false;
}
}